剑指offer-第二章数据结构(数组,字符串,链表,树,栈与队列)及例题
一、数组(最简单的数据结构)
定义:占据一块连续内存并按照顺序存储数据。创建时先指定大小,分配内存。
优点:时间效率高。实现简单的hash(下标为key,对应的数据为value)
缺点:空间效率差。如果我们只在数组中存一个数字,也先分配所有的内存。
扩展:动态数组(解决空间效率差),手段:扩容后,复制内容到新的数组,释放之前的内存。时间性能变差,因此,要尽量减少改变数组容量的次数。
数组与指针:声明一个数组时,数组名字也是一个指针,指向数组的第一个元素。
例题:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序,请完成一个函数,输入这样一个二维数组和一个整数,判断数组中是否含有整数。
思路:
写一个bool Find(int* matrix, int rows, int columns, int number)函数,将一个矩阵的右上角的值a和这个整数b进行对比.
if(a==b) return true;
else if (a>b) 去掉当前列,在剩下的列中进行查找。
else 去掉当前行,在剩下的行中进行查找。
#include <iostream>
using namespace std;
bool Find(int* matrix, int rows, int columns, int number)
{
bool found = false; if(matrix != NULL&& rows > 0 && columns > 0)
{
int row = 0;
int column = columns - 1;
while(row < rows && column >=0)
{
if(matrix[row * columns + column] == number)
{
found = true;
break;
}
else if(matrix[row * columns + column] > number)
-- column;
else
++ row;
}
} return found;
}
//========测试代码=============
void Test(char * testName, int * matrix, int rows, int columns, int number, bool expected)
{
if(testName != NULL)
printf("%s begins: ",testName);
bool result=Find(matrix, rows, columns, number);
if(result == expected)
printf("Passed.\n");
else
printf("Failed.\n");
}
// 1 2 8 9
// 2 4 9 12
// 4 7 10 13
// 6 8 11 15
// 要查找的数在数组中
void Test1()
{
int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
Test("Test1", (int*)matrix, 4, 4, 7, true);
} // 1 2 8 9
// 2 4 9 12
// 4 7 10 13
// 6 8 11 15
// 要查找的数不在数组中
void Test2()
{
int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
Test("Test2", (int*)matrix, 4, 4, 5, false);
} // 1 2 8 9
// 2 4 9 12
// 4 7 10 13
// 6 8 11 15
// 要查找的数是数组中最小的数字
void Test3()
{
int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
Test("Test3", (int*)matrix, 4, 4, 1, true);
} // 1 2 8 9
// 2 4 9 12
// 4 7 10 13
// 6 8 11 15
// 要查找的数是数组中最大的数字
void Test4()
{
int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
Test("Test4", (int*)matrix, 4, 4, 15, true);
} // 1 2 8 9
// 2 4 9 12
// 4 7 10 13
// 6 8 11 15
// 要查找的数比数组中最小的数字还小
void Test5()
{
int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
Test("Test5", (int*)matrix, 4, 4, 0, false);
} // 1 2 8 9
// 2 4 9 12
// 4 7 10 13
// 6 8 11 15
// 要查找的数比数组中最大的数字还大
void Test6()
{
int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
Test("Test6", (int*)matrix, 4, 4, 16, false);
} // 鲁棒性测试,输入空指针
void Test7()
{
Test("Test7", NULL, 0, 0, 16, false);
}
void main()
{
Test1();
Test2();
Test3();
Test4();
Test5();
Test6();
Test7();
}
java代码:
public class Find { public Find() {
// TODO Auto-generated constructor stub
}
public boolean find(int[][] a,int b,int rows,int columns){
if(a==null||rows<0||columns<0)
return false;
int row=0,column=columns-1;
while(row<rows&&column>=0){
if(a[row][column]==b)
return true;
else if(a[row][column]<b)
column--;
else
row++;
}
return false;
} }
二.字符串
定义:若干字符组成的序列
c++/c与Java中字符串的对比:c/c++中每个字符串都是以'\0'作为结尾;Java中却不是这样的,Java中的一切都是对象,对象有长度,编译器可以确定输出的字符个数。
题目:请实现一个函数,把字符串中的每一个空格替换成'%20',例如,输入为"we are happy",则输出为:"we%20are%20happy"
思路:要弄清楚两点:第一,在原串上面干,字符串的长度变长。第二,在新串上面干,自己分配足够地内存。
从头开始遍历字符串,在插入字符后,后面的字符会后移。因此我们采用从后面开始遍历。
先遍历一次字符串,统计字符串中空格的总数,计算替换后字符串的总长度。用指针p1和p2分别指向原字符串的尾部和替换后字符串的尾部。
#include <iostream>
using namespace std; void ReplaceBlank(char string[],int length)
{
if(string==NULL||length<=0)
return ;
int originalLength=0;
int numberOfBlank=0;
int i=0;
while(string[i]!='\0')
{
++ originalLength;
if(string[i]==' ')
++numberOfBlank;
++i;
}
int newLength=originalLength+numberOfBlank*2;
if(newLength>length)
{
return;
}
int indexOfOriginal=originalLength;
int indexOfNew=newLength;
while(indexOfOriginal>=0&&indexOfOriginal<indexOfNew)
{
if(string[indexOfOriginal]==' ')
{
string[indexOfNew--]='0';
string[indexOfNew--]='2';
string[indexOfNew--]='%';
}
else
string[indexOfNew--]=string[indexOfOriginal];
--indexOfOriginal;
}
}
void Test(char* testName, char string[], int length, char expected[])
{
if(testName != NULL)
printf("%s begins: ", testName); ReplaceBlank(string, length); if(expected == NULL && string == NULL)
printf("passed.\n");
else if(expected == NULL && string != NULL)
printf("failed.\n");
else if(strcmp(string, expected) == 0)
printf("passed.\n");
else
printf("failed.\n");
} // 空格在句子中间
void Test1()
{
const int length = 100; char string[length] = "hello world";
Test("Test1", string, length, "hello%20world");
} // 空格在句子开头
void Test2()
{
const int length = 100; char string[length] = " helloworld";
Test("Test2", string, length, "%20helloworld");
} // 空格在句子末尾
void Test3()
{
const int length = 100; char string[length] = "helloworld ";
Test("Test3", string, length, "helloworld%20");
} // 连续有两个空格
void Test4()
{
const int length = 100; char string[length] = "hello world";
Test("Test4", string, length, "hello%20%20world");
} // 传入NULL
void Test5()
{
Test("Test5", NULL, 0, NULL);
} // 传入内容为空的字符串
void Test6()
{
const int length = 100; char string[length] = "";
Test("Test6", string, length, "");
} //传入内容为一个空格的字符串
void Test7()
{
const int length = 100; char string[length] = " ";
Test("Test7", string, length, "%20");
} // 传入的字符串没有空格
void Test8()
{
const int length = 100; char string[length] = "helloworld";
Test("Test8", string, length, "helloworld");
} // 传入的字符串全是空格
void Test9()
{
const int length = 100; char string[length] = " ";
Test("Test9", string, length, "%20%20%20");
} int main(int argc, char* argv[])
{
Test1();
Test2();
Test3();
Test4();
Test5();
Test6();
Test7();
Test8();
Test9(); return 0;
}
Java代码:
public class ReplaceBlank { public ReplaceBlank() {
// TODO Auto-generated constructor stub
}
public String replaceBlank(String s){
if(s==null)
return null;
s=s.replaceAll(" ", "%20");
return s;
}
public static void main(String[] args){
String s="a b c d ed";
ReplaceBlank rb=new ReplaceBlank();
String s1=rb.replaceBlank(s);
System.out.println(s1);
}
}
相关例题:有两个排序的数组A1和A2,内存在A1的末尾有足够的多余的空间来容纳A2,请实现一个函数,把A2中地所有的数字插入到A1中并且所有的数字是排序的。
思路:同样是定义两个指针p1和p2,分别指向字数组A1和A2尾端,sp1和sp2,分别指向数组A1和A2的头部。并且设置一个指针p3指向合并后的字符串(即:A1数组)的尾端。
当满足p1>sp1和p2>sp2的同时。if(*p1>=*p2)*p3=*p1;else *p3=*p2;之后,当满足p1>sp1,*p3=*p1;当满足p2>sp2,*p3=*p2。
#include <stdio.h>
void mergaMatrix(int* matrix1,int* matrix2,
int lenofmtrx1,int lenofmtrx2,int sizeofmatrix1)
{
if(sizeofmatrix1 != 0 && matrix1 != NULL && lenofmtrx1 !=0
&& matrix2 != NULL && lenofmtrx2 != 0 )
{
int* pNewMatrix1 = matrix1 + lenofmtrx1 + lenofmtrx2 -1;
int* pMatrix1 = matrix1 + lenofmtrx1 - 1;
int* pMatrix2 = matrix2 +lenofmtrx2 - 1;
while(pMatrix1 >= matrix1 && pMatrix2 >= matrix2)
{
if(*pMatrix1 >= *pMatrix2)
*pNewMatrix1-- = *pMatrix1--;
else
*pNewMatrix1-- = *pMatrix2--;
}
while(pMatrix1 >= matrix1)
{
*pNewMatrix1-- = *pMatrix1--;
}
while(pMatrix2 >= matrix2)
{
*pNewMatrix1-- = *pMatrix2--;
}
}
return;
}
//单元测试
void test(int* matrix1,int* matrix2,
int lenofmtrx1,int lenofmtrx2,int sizeofmatrix1)
{
if(matrix1 != NULL)
{
for( int i=0; i<lenofmtrx1;i++)
{
printf("%d ",*(matrix1+i));
}
}
printf("\n");
if(matrix2 != NULL){
for( int i=0; i<lenofmtrx2;i++)
{
printf("%d ",*(matrix2+i));
}
}
printf("\n");
mergaMatrix(matrix1,matrix2,lenofmtrx1,lenofmtrx2,sizeofmatrix1);
for( int i=0; i<lenofmtrx1+lenofmtrx2;i++)
{
printf("%d ",*(matrix1+i));
}
printf("\n");
}
//一般情况
void test1()
{
const int sizeofmatrix1 = 100;
int lenofmtrx1 = 3;
int matrix1[sizeofmatrix1] = {1,3,5};
int lenofmtrx2 = 4;
int matrix2[] = {2,4,6,8};
test(matrix1,matrix2,lenofmtrx1,lenofmtrx2,sizeofmatrix1);
}
//其中一个数组的书全部小于另外一个
void test2()
{
const int sizeofmatrix1 = 100;
int lenofmtrx1 = 3;
int matrix1[sizeofmatrix1] = {1,3,5};
int lenofmtrx2 = 4;
int matrix2[] = {6,7,8,9};
test(matrix1,matrix2,lenofmtrx1,lenofmtrx2,sizeofmatrix1);
}
//其中一个为空
void test3()
{
const int sizeofmatrix1 = 100;
int lenofmtrx1 = 3;
int matrix1[sizeofmatrix1] = {1,3,5};
test(matrix1,NULL,lenofmtrx1,0,sizeofmatrix1);
}
//两个都为空
void test4()
{
const int sizeofmatrix1 = 100;
test(NULL,NULL,0,0,sizeofmatrix1);
}
int main()
{
test1();
test2();
test3();
test4();
return 0;
}
Java代码:
import java.util.Arrays;
public class Merge { public Merge() {
// TODO Auto-generated constructor stub
}
public void mergeArray(int[] a,int b[]){
if(a==null||b==null)
return;
int[] c=new int[a.length+b.length];
int k=0;
for(int i=0;i<a.length;i++){
c[k++]=a[i];
}
for(int j=0;j<b.length;j++){
c[k++]=b[j];
}
Arrays.sort(c);
for(int z:c){
System.out.print(z+" ");
}
}
public static void main(String[] args)
{
int[] a={2,4,6};
int[] b={3,5,7,9}; Merge m=new Merge();
m.mergeArray(a, b);
}
}
三.链表(创建,尾部添加,删除,打印(从头到尾),打印(从尾到头(stack),打印从尾到头(递归(会导致栈溢出))面试中最频繁的数据结构。
定义:由指针把若干个节点链接成链状的结构。
例题:从尾到头打印一个链表。
思路:
1.创建链表返回头指针ListNode* createLink(int a[],int k)(初始化头指针Head和行动指针p,当满足当前长度小于链表的长度时。创建一个节点pNew并赋初值,if(Head==NULL){Head=pNew;p=pNew;}else{p->next=pNew;p=p->next;})。
2.尾部添加无返回值void addTail(ListNode** Head,int value)(用到了指针的指针ListNode**head,创建一个节点pNew并赋初值。if(*Head==NULL){*Head=pNew;else创建行动指针p并指向头结点,找到插入节点的头一个接点,在它的后面插入节点。})。
3.删除一个已知值的节点返回空,void removeNode(ListNode** Head,int value)(同样用到指针的指针,if(Head==NULL&&*Head==NULL)初始化要删除的指针toBeDelete,if(*Head==value){toBeDelete=*Head,*Head=*Head->next)},else,初始化行动指针p并指向头指针,找到要删除的节点的头一个节点p,if(p->next!=null&&p->next->value==value){ toBeDelete=p->next,p-next=p->next->next}。)
4.打印(从头到尾)返回空,void printLink(ListNode* pHead)(初始化行动指针p并指向头节点,当满足p不为空的时候,输出p->value,并且移动p=p->next)
5.打印(从尾到头(栈))返回空,void printLinkReservese(ListNode* pHead)(定义一个栈 std::stack<ListNode *>nodes,初始化行为指针p并指向头节点,当满足p不为空时,将nodes.push(p),并且移动p=p->next;当nodes.empty不为空时,将行为指针指向顶并且输出p->value,nodes.pop();)
6.打印(从尾到头(递归)),返回空。void printLinkReservese_Recurse(ListNode *pHead)(if(pHead!=NULL){if(pHead->next!=NULL) printLinkReservse_Reservese_Recurse(pHead->next);}输出pHead->value.)
#include<iostream.h>
#include <stdio.h>
#include <stack>
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
};
void RemoveNode(ListNode ** pHead,int value)
{
if(pHead==NULL||*pHead==NULL)
return;
ListNode* toBeDelete=NULL;
if((*pHead)->m_nValue==value)
{
toBeDelete=*pHead;
*pHead=(*pHead)->m_pNext;
}
else
{
ListNode*p=*pHead;
while(p->m_pNext!=NULL&&p->m_pNext->m_nValue!=value)
p=p->m_pNext;
if(p->m_pNext!=NULL&&p->m_pNext->m_nValue==value)
{
toBeDelete=p->m_pNext;
p->m_pNext=p->m_pNext->m_pNext;
}
//这一步很重要,防止操作已经释放的内存空间
if(toBeDelete!=NULL)
{
delete toBeDelete;
toBeDelete=NULL;
}
}
}
void AddToTail(ListNode ** pHead,int value)
{
ListNode * pNew=new ListNode();
pNew->m_nValue=value;
pNew->m_pNext=NULL; if(*pHead==NULL)
{
*pHead=pNew;
}
else
{
ListNode * pNode = *pHead;
while(pNode->m_pNext != NULL)
pNode=pNode->m_pNext;
pNode->m_pNext=pNew;
}
} ListNode * CreateLink(int a[],int k)
{
ListNode * Head=NULL,*q=NULL;
for(int i=0;i<k;i++)
{
ListNode * pNew=new ListNode();
pNew->m_nValue=a[i];
pNew->m_pNext=NULL; if(Head==NULL)
{
Head=pNew;
q=pNew;
}
else
{
q->m_pNext=pNew;
q=q->m_pNext;
}
}
return Head;
}
//从头到尾打印列表
void printLink(ListNode * pHead)
{
ListNode *p=pHead;
while(p)
{
cout<<p->m_nValue<<" ";
p=p->m_pNext;
}
cout<<endl;
}
//从尾到头打印链表(用栈)。
void PrintListReversesingly_Iteratively(ListNode * pHead)
{
std::stack<ListNode *> nodes;
ListNode *p=pHead;
while(p)
{
nodes.push(p);
p=p->m_pNext;
}
while(!nodes.empty())
{
p=nodes.top();
cout<<p->m_nValue<<" ";
nodes.pop();
}
}
//从尾到头打印链表(用递归,隐式调用栈)。
void printListReversesingly_Recursively(ListNode * pHead)
{
if(pHead!=NULL)
{
if(pHead->m_pNext!=NULL)
{
printListReversesingly_Recursively(pHead->m_pNext);
}
}
cout<<pHead->m_nValue<<" "; } void main()
{
int a[]={1,2,3};
ListNode * Head=CreateLink(a,3);
printLink(Head); AddToTail(&Head,6);
printLink(Head);
printListReversesingly_Recursively(Head);
cout<<endl;
RemoveNode(&Head,3); printLink(Head);
PrintListReversesingly_Iteratively(Head);
cout<<endl; }
java代码实现:
import java.util.Stack; public class Link {
public class LinkNode{
int m_nVlaue;
LinkNode m_pNext;
}
//创建链表
public LinkNode createLink(int[] a){
if(a==null){
return null;
}
LinkNode pHead=null,p=null;
for(int i=0;i<a.length;i++){
LinkNode pNode=new LinkNode();
pNode.m_nVlaue=a[i];
if(pHead==null){
pHead=pNode;
p=pHead;
}
else{
p.m_pNext=pNode;
p=p.m_pNext;
}
}
return pHead; }
//删除一个节点
public LinkNode deleteLinkNode(LinkNode pHead,int value){
if(pHead==null)
return pHead; if(pHead.m_nVlaue==value)
{
pHead=pHead.m_pNext;
}
LinkNode p=pHead;
while(p.m_pNext!=null&&p.m_pNext.m_nVlaue!=value){
p=p.m_pNext;
}
if(p.m_pNext!=null&&p.m_pNext.m_nVlaue==value)
{ p.m_pNext=p.m_pNext.m_pNext;
}
return pHead; }
public void addTail(LinkNode pHead,int value){
LinkNode pNew=new LinkNode();
pNew.m_nVlaue=value;
pNew.m_pNext=null;
LinkNode pNode=pHead; if(pHead==null)
pHead=pNew; while(pNode.m_pNext!=null){
pNode=pNode.m_pNext;
}
pNode.m_pNext=pNew; }
//逆序打印链表
public void printLinkReservse(LinkNode pHead){
if(pHead==null)
return;
Stack<Integer> stack=new Stack<Integer>();
while(pHead!=null){
stack.push(pHead.m_nVlaue);
pHead=pHead.m_pNext;
}
while(!stack.isEmpty()){
int a=stack.peek();
System.out.print(a+" ");
stack.pop();
}
}
public static void main(String[] args){
int[] a={3,4,6,7,8,9,0};
Link link=new Link();
LinkNode pHead=link.createLink(a);
link.printLinkReservse(pHead);
pHead=link.deleteLinkNode(pHead,3);
link.printLinkReservse(pHead);
link.addTail(pHead, 10);
link.printLinkReservse(pHead);
} }
四.树(二叉树(创建,打印,删除))
定义:除了根节点之外,每个结点都有一个父节点,除了叶子节点外所有的节点都有一个或者多个子节点。
二叉树:每个节点最多有两个叶子节点
遍历:按照某个顺序访问树中的所有节点。
三种常见的遍历:前序遍历,中序遍历,后续遍历(可以用递归和循环两种方式实现)
可实现的题目:二叉树的深度,树的子结构,二叉树的后续遍历。从上到下遍历二叉树(宽度优先遍历)。
二查搜索树:左子结点总是小于等于根节点,而右子节点总是大于等于根节点。找到一个节点的平均时间为O(logn)
二叉树特例:堆和红黑二叉树堆:最大堆和最小堆。可以解决(快速找最大值和最小值问题)
红黑树:将树中的节点定义为红、黑两种颜色,通过规则确保从根节点到叶子节点的最长路径不超过最短路径的两倍。在C++ 中的STL(Standard Template libary(标准模板库),set,multiset,map,multimap等数据结构都是基于红黑树实现的。)
重建二叉树
题目:输入某个二叉树的前序遍历和中序遍历的结果,重建该二叉树,假设输入前序和中序遍历的结果中没有重复的数字。例如:输出得 前序遍历为{1,2,4,7,3,5,6,8}和中序遍历{4,7,2,1,5,3,8,6},重建二叉树。
思路:
1.创建二叉树,返回值为BinaryTreeNode *,BinaryTreeNode* construct(int* preorder,int* inorder,int length)(如果前序遍历和中序遍历的头指针都不空,及length大于0,则调用函数BinaryTreeNode *ConstructCore(int*startPreorder,int*endPreorder,int*startInorder,int*endInorder)(首先将前序遍历的第一个值赋值给rootValue,初始化根节点,if(startPreorder==endPreorder){if(startInorder==endInorder&&*startPreorder==*startInorder) return root;else 抛空异常。) 据中序遍历找到根节点。定义一个行动指针指p向中序遍历的头结点。当满足p<=endInorder时,找到根节点(root),分别求出左、右子树的长度leftLength和rightLength,当leftLength和rightLength不为零时,分别创建左右子树。root->left=调用ConstructCore函数,root->right=调用ConstructCore函数。
2.打印二叉树。无返回值。首先创建要打印一个节点的函数void printTreeNode(BinaryTreeNode* pNode)(当pNode不为空时,打印pNode的值,如果pNode->left为空,打印pNode->left->value,右指针同左指针一样操作。else 抛空。),然后再创建一个函数,返回值为空。void PrintTree(BinaryTreeNode *root){如果根节点不 为空打印根节点的值,左指针不为空,递归打印左子树。右指针同理。}。
3.删除二叉树。无返回值。函数void DeleteNode(BinaryTreeNode* pRoot){如果pRoot不为空,则将左右指针分别赋值给一个新的指针,然后删除pRoot,并且置空。递归删除左右子树。}
#include <stdio.h>
#include <iostream>
using namespace std;
struct BinaryTreeNode
{
int m_nValue;
BinaryTreeNode* m_pLeft;
BinaryTreeNode* m_pRight; };
BinaryTreeNode* ConstructCore(int* startPreorder,int* endPreorder,int* startInorder,int* endInorder)
{
int rootValue=startPreorder[0];
BinaryTreeNode* root=new BinaryTreeNode();
root->m_nValue=rootValue;
root->m_pLeft=root->m_pRight=NULL;
if(startPreorder==endPreorder)
{
if(startInorder==endInorder&&*startPreorder==*startInorder)
{
return root;
}
else
throw std::exception("Invalid put!");
}
//通过中序遍历序列找到根节点
int* rootInorder=startInorder;
while(rootInorder<=endInorder&&*rootInorder!=rootValue)
{
++rootInorder;
}
if(rootInorder==endInorder&&*rootInorder!=rootValue)
{
throw std::exception("Invalid put");
}
int leftLength=rootInorder-startInorder;
int rightLength=endInorder-rootInorder;
int* leftPreorderEnd=startPreorder+leftLength;
if(leftLength>0)
{
//递归构建左子树
root->m_pLeft=ConstructCore(startPreorder+1,leftPreorderEnd,startInorder,rootInorder-1);
}
if(rightLength>0)
{
//递归构建右子树
root->m_pRight=ConstructCore(leftPreorderEnd+1,endPreorder,rootInorder+1,endInorder);
}
return root; }
BinaryTreeNode* Construct(int* preorder,int* inorder,int length)
{
if(preorder==NULL||inorder==NULL||length<=0)
{
throw std::exception("Invalid put!");
}
return ConstructCore(preorder,preorder+length-1,inorder,inorder+length-1);
} void PrintTreeNode(BinaryTreeNode* pNode)
{
if(pNode != NULL)
{
printf("value of this node is: %d\n", pNode->m_nValue); if(pNode->m_pLeft != NULL)
printf("value of its left child is: %d.\n", pNode->m_pLeft->m_nValue);
else
printf("left child is null.\n"); if(pNode->m_pRight != NULL)
printf("value of its right child is: %d.\n", pNode->m_pRight->m_nValue);
else
printf("right child is null.\n");
}
else
{
printf("this node is null.\n");
} printf("\n");
}
//递归打印左右子树
void PrintTree(BinaryTreeNode* pRoot)
{
PrintTreeNode(pRoot); if(pRoot != NULL)
{
if(pRoot->m_pLeft != NULL)
PrintTree(pRoot->m_pLeft); if(pRoot->m_pRight != NULL)
PrintTree(pRoot->m_pRight);
}
}
//递归删除左右子树
void DestroyTree(BinaryTreeNode* pRoot)
{
if(pRoot != NULL)
{
BinaryTreeNode* pLeft = pRoot->m_pLeft;
BinaryTreeNode* pRight = pRoot->m_pRight; delete pRoot;
pRoot = NULL; DestroyTree(pLeft);
DestroyTree(pRight);
}
} void main()
{
const int length = 8;
int preorder[length] = {1, 2, 4, 7, 3, 5, 6, 8};
int inorder[length] = {4, 7, 2, 1, 5, 3, 8, 6}; BinaryTreeNode *root = Construct(preorder, inorder, length);
PrintTree(root);
DestroyTree(root);
}
java代码:
public class CreateBiTree { public class BiNode{
int m_nValue;
BiNode m_pLeft;
BiNode m_pRight;
}
public BiNode constructBiTree(int[] a,int start,int[] b,int end,int length){
if(a==null||b==null||a.length!=b.length||length<0)
return null;
int value=a[start];
BiNode pNode=new BiNode();
pNode.m_nValue=value;
pNode.m_pLeft=pNode.m_pRight=null;
if(length==1&&a[start]==b[end]){
return pNode;
}
//根据中序遍历的结果,找到根节点
int i=0;
while(i<length){
if(b[end-i]==value)
break;
i++;
}
if(i==length)
throw new RuntimeException("inVaild put!");
int right=i;
int left=length-i-1;
if(right>0)
pNode.m_pRight=constructBiTree(a,start+length-i,b,end,i);
if(left>0)
pNode.m_pLeft=constructBiTree(a,start+1,b,end-i-1,length-i-1);
return pNode;
}
public void printNode(BiNode pNode){
if(pNode==null)
System.out.println("this node is null");
else
System.out.println("this node is:"+pNode.m_nValue);
if(pNode.m_pLeft==null)
System.out.println("this left node is null");
else
System.out.println("this left node is:"+pNode.m_pLeft.m_nValue);
if(pNode.m_pRight==null)
System.out.println("this right node is null");
else
System.out.println("this right node is:"+pNode.m_pRight.m_nValue);
}
//打印二叉树
public void printBiTree(BiNode pNode){
printNode(pNode);
if(pNode.m_pLeft!=null)
printBiTree(pNode.m_pLeft);
if(pNode.m_pRight!=null)
printBiTree(pNode.m_pRight);
}
public static void main(String[] args){
int[] a={1,8,3,5,7,9,10};
int[] b={3,8,5,1,9,7,10};
CreateBiTree bt=new CreateBiTree();
BiNode root=bt.constructBiTree(a,0,b,6,7);
bt.printBiTree(root);
} }
五.栈与队列
栈定义:用来存储函数调用时各个函数的参数,返回地址及临时变量等。
特点:先进后出,不考虑排序。
队列特点:先进先出。
题目:用两个栈实现一个队列,队列申明如下,实现它的两个函数appendTail和deleteHead,即:队列的尾部插入节点,头部删除节点。
思路:自定义一个函数模板CQueue,它有public属性或方法:构造函数,析构函数,在尾部添加节点void appendTail(const T& node),删除队列的头节点T deleteHead().私有属性:stack<T> stack1;stack<T>stack2;由于定义CQueue,为模板的原因,在尾部添加节点实现函数为:template< typename T> void cQueue<T>::appendTail(const T& node){stack1.push(node)},删除队列头部的节点template<T> T cQueue<T> ::deleteHead()如果stack2<=0,当stack1.size()满足大于0时,将stack1,中的所有元素压入stack2.如果stack2==0,抛空。定义一个T类型的head并且赋初值为stack2.top,stack2.pop(),返回Head。
#include <iostream>
#include <stack>
#include <exception>
using namespace std; template <typename T> class CQueue
{
public:
CQueue(void);
~CQueue(void); // 在队列末尾添加一个结点
void appendTail(const T& node); // 删除队列的头结点
T deleteHead(); private:
stack<T> stack1;
stack<T> stack2;
}; //成员函数定义.
template <typename T>
CQueue<T>::CQueue(void)
{
} template <typename T>
CQueue<T>::~CQueue(void)
{
} //向队列中插入元素.
template <typename T>
void CQueue<T>::appendTail(const T& node)
{
stack1.push(node);
} //从队列中删除元素.
template <typename T>
T CQueue<T>::deleteHead()
{
//先判断stack2是否为空.
//若其不为空,则从中弹出元素;
//若为空,则将stack1中元素依次弹出并存入stack2中.
if(stack2.size() <= 0)
while (stack1.size() > 0)
{//当stack2为空时,将stack1中所有元素存入stack2中.
T data = stack1.top();
stack1.pop();
stack2.push(data);
}
if(stack2.size() == 0)
throw new exception("queue is empty!"); T head = stack2.top();
stack2.pop(); return head;
} //测试函数.
void Test(char actual, char expected)
{
if(actual == expected)
cout << "Test passed." << endl;
else
cout << "Test failed.\n" << endl;
} int main()
{
CQueue<char> queue; queue.appendTail('a');
queue.appendTail('b');
queue.appendTail('c'); char head = queue.deleteHead();
Test(head, 'a'); head = queue.deleteHead();
Test(head, 'b'); queue.appendTail('d');
head = queue.deleteHead();
Test(head, 'c'); queue.appendTail('e');
head = queue.deleteHead();
Test(head, 'd'); head = queue.deleteHead();
Test(head, 'e'); return 0;
}
Java代码:
import java.util.Stack; public class TwoStackToQueue<T> {
Stack<T> stack1=new Stack<T>();
Stack<T> stack2=new Stack<T>();
public void addTail(T item)
{
stack1.push(item);
}
public T deleteHead(){
if(stack2.isEmpty()){
if(!stack1.isEmpty()){
while(!stack1.isEmpty()){
T data=stack1.peek();
stack1.pop();
stack2.push(data);
}
} }
if(stack2.isEmpty())
throw new RuntimeException("inVaild put");
T data=stack2.peek();
stack2.pop();
return data;
}
public static void main(String[] args){
TwoStackToQueue<Integer> queue=new TwoStackToQueue<Integer>();
queue.addTail(3);
queue.addTail(4);
queue.addTail(5);
int data=(int)queue.deleteHead();
System.out.println(data+" "); }
}
剑指offer-第二章数据结构(数组,字符串,链表,树,栈与队列)及例题的更多相关文章
- 剑指offer第二章
剑指offer第二章 1.二维数组中的查找 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含 ...
- 《剑指offer》第九题(用两个栈实现队列)
// 面试题:用两个栈实现队列 // 题目:用两个栈实现一个队列.队列的声明如下,请实现它的两个函数appendTail // 和deleteHead,分别完成在队列尾部插入结点和在队列头部删除结点的 ...
- 剑指offer 面试题9:用两个栈实现队列
题目描述 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. 使用栈实现队列的下列操作:push(x) -- 将一个元素放入队列的尾部.pop() -- 从队列首部移 ...
- 剑指offer—第二章算法之二分查找(旋转数组的最小值)
旋转数组的最小数字 题目:把一个数组最开始的若干元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如:数组{3,4,5,1,2}为{1,2,3,4, ...
- 剑指offer第二版-3.数组中重复的数
面试题3:数组中重复的数 题目要求: 在一个长度为n的数组中,所有数字的取值范围都在[0,n-1],但不知道有几个数字重复或重复几次,找出其中任意一个重复的数字. 解法比较: /** * Copyri ...
- 剑指offer—第二章算法之快速排序
算法:排序和查找(二分查找,归并排序,快速排序),位运算等. 查找:顺序查找,哈希查找,二叉排序树查找,哈希表. 二分查找可以解决:"旋转数组中的最小数字","数字在排序 ...
- 剑指offer:按之字形打印二叉树(栈|双向队列+中序遍历)
1. 题目描述 /** 请实现一个函数按照之字形打印二叉树, 即第一行按照从左到右的顺序打印, 第二层按照从右至左的顺序打印, 第三行按照从左到右的顺序打印, 其他行以此类推. */ 2. 双向队列 ...
- 《剑指offer》面试题7 用两个栈实现队列 Java版
书中方法:队列是先进先出的,栈是先进后出的,试想把一串数压入A栈,接着一个个出栈并压入B栈,便会完成"头在下"到"头在上"的转变.B栈内还有元素时,直接出栈表示 ...
- 剑指Offer面试题:6.用两个栈实现队列
一.题目:用两个栈实现队列 题目:用两个栈实现一个队列.队列的声明如下,请实现它的两个函数appendTail和deleteHead,分别完成在队列尾部插入结点和在队列头部删除结点的功能. 原文是使用 ...
- 【剑指offer 面试题7】用两个栈实现队列
#include <iostream> #include <stack> using namespace std; template <typename T> cl ...
随机推荐
- 存在单点故障的namenode宕机恢复测试
前提:如果namenode没有做HA,那么至少应该启用secondarynamenode,以便namenode宕机之后手动恢复数据 实验环境:3个节点(cenos 6.10) 测试前数据: 1.为了确 ...
- 重置root密码后仍然不能登陆
一.忘记密码:二.输入正确用户名和密码时依旧无法登录. 一.忘记密码 进入单用户模式重置密码: 开机启动时,按‘E’键(倒计时结束前)进入界面 选择第二项,按‘E’键再次进入 在最后一行添加‘ 1’( ...
- How to create a notification with NotificationCompat.Builder?AAAA
Ask Question up vote 49 down vote favorite 19 I need to create a simple notification which will be s ...
- h5新特性--- 多媒体元素
在H5中只有一行代码即可实现在页面中插入视频 <video src="插入的视频的名字" controls></video> 可以指明视频的宽度和高度 &l ...
- 带SSL证书的httpclient 远程接口工具类
package com.iups.wx.util; import java.io.IOException; import java.io.UnsupportedEncodingException; i ...
- [Bzoj]5343: [Ctsc2018]混合果汁
5343: [Ctsc2018]混合果汁 题目描述 小 R 热衷于做黑暗料理,尤其是混合果汁. 商店里有 \(n\) 种果汁,编号为 \(0,1,\cdots,n-1\) .\(i\) 号果汁的美味度 ...
- 微信app支付java后台流程、原理分析及nei网穿透
一.流程步骤 本实例是基于springmvc框架编写 1.执行流程 当手机端app(就是你公司开发的app)在支付页面时,调起服务端(后台第1个创建订单接口)接口,后台把需要调起微 ...
- TCP_AIO_Server_ZC_01
ZC: 这个例子是,1个skt 投递 1个未决的接收操作 1.package aio.Client; 1.1.这是一个 测试的客户端的代码: package aio.Client; import ja ...
- DTO数据传输对象
如果有多个对象需要传输到页面上需要用DTO传输
- JavaScript 哈希表(散列表)实现和应用
查找的效率与比较次数密切相关.基于比较的程序,运算效率是比较低的.比如平时可以通过indexOf查找一个数据.但这是一个基于比较的一个实现.如果是淘宝那样有上亿个商品,那么用indeOf 来查数据就会 ...