数据结构回顾
关于指针的一点知识
易错点:不要以为指针作为形参传递就可以更改指针本身,只能改变指针指向对象的取值,指针本身的指向是不会变的!!记住任何参数传递都是值传递!
这种情况下的解决方法有两种:1)传递指针的指针或者指针的引用。2)将改变后的指针值作为函数的返回值
递归传参 1)全局变量;2)递归函数参数<函数内外定义 both ok>;3)返回值
第二点:声明(定义)时候的*增加指针维度;使用时候的*和[]减少指针维度;使用时候的&增加指针维度。
利用new方法获得的都是指针类型变量,指针指向的对象存储在堆空间;通过声明得到的是对象变量,对象存储在栈空间。
数组与指针
新建int型变量: int* a=new int; *a=3; 事实上很少这么用的,直接int a=3即可
新建int数组:int* a=new int[2]; int*p=a;*p=1;*(p+1)=2;*(p+2)=3;
对于int a[3][4]
&a 的类型是: 类型(int*)[3][4]; 步长为3*4*4=48,即跳跃整个数组; sizeof(&a)=4;
a的类型是: 类型(int*)[4] 步长为4*4=16,即跳跃一整行; sizeof(a)=3*4*4=48;
*a的类型是: 类型int*; 步长为4,也即跳跃单个元素; sizeof(*a)=4
**a的类型是: 类型int; 不是指针,不存在步长的说法; sizeof(**a)=4
对于int** a[3][4]
[]的结核性从左到右,比*高,因此这是一个二维指针数组;数组中的元素是指向int型指针的指针
对于int*(*a)[3][4]
这是一个数组指针,指针指向一个二维数组,数组中的元素是int*类型
int* (*a)[3][4]
int a=3; int *p=&a;
printf("%d",p);得到的是一个地址
printf("%d",*p);得到p指向地址存储变量的内容,也就是a:3
free(p);之后,p这个地址变量指向的地址不知道了,变成了野指针,供操作系统再次分配。无论是对p的调用还是对*p的调用都会造成段错误。
p=NULL之后,printf("%d",p)得到0;printf("%d",*p);引发段错误。
还有记得,c语言是一句一句顺序执行的
指针相关的运算符: 指针变量,存放的是所指向变量的地址 &取地址运算符:去除变量所在的地址 *取出所指向地址处变量的内容,而不是取出指针变量中的地址 指针相关运算符的优先级别以及结合顺序: ()、[]、->、. :优先级为1,结合性为自左向右 * & 优先级为2,结合性为自右向左 ->是指向结构体变量的运算符 如果p是指向结构体变量的指针,那么(*p).num和p->num的效果相同。 数组相关的指针: 对于a[2][3] a代表的是二维数组首行的地址,a+1代表第一行的首地址,变化的幅度为行。 a[0]、a[1]是以为数组名。变化的幅度为一个元素。 a[1]和*(a+1)的效果是一样的。 int a[n] 定义整型数组a,它有n个元素 int *a[n]定义指针数组a,它由n个指向整形数据的指针元素构成 int (*p)[n] p为指向含n个元素的以为数组的指针变量 int (*p) ();指向函数的指针 int **p指向指针的指针 int *p() 返回类型为指针型变量的函数 关于空指针,指针变量为空,指针指向空 int *p; p=null; *p=null; 空指针是指指针指向的内容为空,空指针是不能操作的,否则会引发所谓的空指针异常(就是一个指针是空指针,你还要去操作它,既然它指向的是空对象,它就不能使用这个对象的方法。) p=null,一般用于指针变量的初始化,否则系统会随机给它分配一个地址。但是它可以指向其他变量。在free之后,也一般蒋指针置为空,代表指着目前不指向任何对象。防止误操作。 free(p)的含义是值蒋指针指向的内存空间清空,交还给操作系统,而指针本身仍存在,因为指针是一个变量,只有程序结束时才被销毁,只不过现在指针指向的内容是无用的,未定义的。因此,释放内存后通常把指针指向 NULL,即p=NULL,防止指针在 后面不小心又被引用。 指针为空(p=NULL)代表该指针没有指向任何变量。 空指针(*p=NULL)(指针内容为空)代表指针指向的变量为空。
#include "stdio.h" #include "string.h" #include "ctype.h" #include "stdlib.h" #include "math.h" #include "time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 /* 存储空间初始分配量 */ typedef int Status;/* Status是函数的类型,其值是函数结果状态代码,如OK等 */ typedef int ElemType;/* ElemType类型根据实际情况而定,这里假设为int */ Status visit(ElemType c) { printf("%d ",c); return OK; } typedef struct Node { ElemType data; struct Node *next; }Node, *LinkList; /* 初始化顺序线性表 */ Status InitList(LinkList *L) { //创建一个头节点,不过该节点并不存放元素 *L=(Node*)malloc(sizeof(Node)); (*L)->next=NULL; return OK; } /* 初始条件:顺序线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */ Status ListEmpty(LinkList L) { if(L->next==NULL){ return TRUE; }else{ return FALSE; } } /* 初始条件:顺序线性表L已存在。操作结果:将L重置为空表,但是并不彻底删除 */ Status ClearList(LinkList *L) { if(ListEmpty(*L)){ return ERROR; } Node *p=(*L)->next; Node* q=NULL; while(p){ q=p->next; free(p); p=q; } (*L)->next=NULL; return OK; } /* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */ int ListLength(LinkList L) { if(ListEmpty(L)){ return 0; } int length=0; Node *p=L->next; while(p){ length++; p=p->next; } return length; } /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */ /* 操作结果:用e返回L中第i个数据元素的值 */ Status GetElem(LinkList L,int i,ElemType *e) { Node *p=L->next; int j=1; while(p&&j<i){ p=p->next; j++; } if(j==i){ *e=p->data; }else{ return ERROR; } return OK; } /* 初始条件:顺序线性表L已存在 */ /* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */ /* 若这样的数据元素不存在,则返回值为0 */ int LocateElem(LinkList L,ElemType e) { int i=1; Node *p=L->next; while(p){ if(p->data==e){ return i; } i++; p=p->next; } if(!p){ return 0; } } /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L), */ /* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */ Status ListInsert(LinkList *L,int i,ElemType e) { if(i>ListLength(*L)+1){ return ERROR; } Node *p=*L; int j=1; while(p&&j<i){ p=p->next; j++; } Node *node=(Node*)malloc(sizeof(Node)); node->data=e; node->next=p->next; p->next=node; return OK; } /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */ /* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */ Status ListDelete(LinkList *L,int i,ElemType *e) { if(ListEmpty(*L)||i>ListLength(*L)){ return ERROR; } int j=1; Node *p=*L; while(p&&j<i){ p=p->next; j++; } Node *q=p->next; p->next=q->next; *e=q->data; free(q); q=NULL; return OK; } /* 初始条件:顺序线性表L已存在 */ /* 操作结果:依次对L的每个数据元素输出 */ Status ListTraverse(LinkList L) { if(ListEmpty(L)){ return ERROR; } Node *p=L->next; while(p){ visit(p->data); p=p->next; } printf("\n"); return OK; } /* 随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */ void CreateListHead(LinkList *L, int n) { //首先进行初始化 InitList(L); srand(time(0)); int i; for(i=0;i<n;i++){ Node *node=(Node*)malloc(sizeof(Node)); if(!node){ exit(OVERFLOW); } node->data=rand()%100+1; node->next=(*L)->next; (*L)->next=node; } } /* 随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法) */ void CreateListTail(LinkList *L, int n) { InitList(L); srand(time(0)); int i; Node *p=*L; for(i=0;i<n;i++){ Node *node=(Node*)malloc(sizeof(Node)); if(!node){ exit(OVERFLOW); } node->data=rand()%100+1; node->next=NULL; p->next=node; p=node; } } int main() { LinkList L; ElemType e; Status i; int j,k; i=InitList(&L); printf("初始化L后:ListLength(L)=%d\n",ListLength(L)); for(j=1;j<=5;j++) i=ListInsert(&L,1,j); printf("在L的表头依次插入1~5后:L.data="); ListTraverse(L); printf("ListLength(L)=%d \n",ListLength(L)); i=ListEmpty(L); printf("L是否空:i=%d(1:是 0:否)\n",i); i=ClearList(&L); printf("清空L后:ListLength(L)=%d\n",ListLength(L)); i=ListEmpty(L); printf("L是否空:i=%d(1:是 0:否)\n",i); for(j=1;j<=10;j++) ListInsert(&L,j,j); printf("在L的表尾依次插入1~10后:L.data="); ListTraverse(L); printf("ListLength(L)=%d \n",ListLength(L)); ListInsert(&L,1,0); printf("在L的表头插入0后:L.data="); ListTraverse(L); printf("ListLength(L)=%d \n",ListLength(L)); GetElem(L,5,&e); printf("第5个元素的值为:%d\n",e); for(j=3;j<=4;j++) { k=LocateElem(L,j); if(k) printf("第%d个元素的值为%d\n",k,j); else printf("没有值为%d的元素\n",j); } k=ListLength(L); /* k为表长 */ for(j=k+1;j>=k;j--) { i=ListDelete(&L,j,&e); /* 删除第j个数据 */ if(i==ERROR) printf("删除第%d个数据失败\n",j); else printf("删除第%d个的元素值为:%d\n",j,e); } printf("依次输出L的元素:"); ListTraverse(L); j=5; ListDelete(&L,j,&e); /* 删除第5个数据 */ printf("删除第%d个的元素值为:%d\n",j,e); printf("依次输出L的元素:"); ListTraverse(L); i=ClearList(&L); printf("\n清空L后:ListLength(L)=%d\n",ListLength(L)); CreateListHead(&L,20); printf("整体创建L的元素(头插法):"); ListTraverse(L); i=ClearList(&L); printf("\n删除L后:ListLength(L)=%d\n",ListLength(L)); CreateListTail(&L,20); printf("整体创建L的元素(尾插法):"); ListTraverse(L); return 0; }
1)上例实现的是带有头节点(头节点不存放具体元素)的单链表。
2)注意链表的清空操作。
关于链表的一些面试题目:
(注意以下实现都是基于不包含头节点的链表)
A。链表的逆序(关键在于3个指针,指向相邻的3个元素)
#include "stdio.h" #include "string.h" #include "ctype.h" #include "stdlib.h" #include "math.h" #include "time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 /* 存储空间初始分配量 */ typedef int Status;/* Status是函数的类型,其值是函数结果状态代码,如OK等 */ typedef int ElemType;/* ElemType类型根据实际情况而定,这里假设为int */ Status visit(ElemType c) { printf("%d ",c); return OK; } typedef struct Node { ElemType data; struct Node *next; }Node, *LinkList; /* 初始化顺序线性表 */ Status InitList(LinkList *L) { //创建一个头节点,不过该节点并不存放元素 *L=(Node*)malloc(sizeof(Node)); (*L)->next=NULL; return OK; } /* 初始条件:顺序线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */ Status ListEmpty(LinkList L) { if(L->next==NULL){ return TRUE; }else{ return FALSE; } } /* 初始条件:顺序线性表L已存在。操作结果:将L重置为空表,但是并不彻底删除 */ Status ClearList(LinkList *L) { if(ListEmpty(*L)){ return ERROR; } Node *p=(*L)->next; Node* q=NULL; while(p){ q=p->next; free(p); p=q; } (*L)->next=NULL; return OK; } /* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */ int ListLength(LinkList L) { if(ListEmpty(L)){ return 0; } int length=0; Node *p=L->next; while(p){ length++; p=p->next; } return length; } /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */ /* 操作结果:用e返回L中第i个数据元素的值 */ Status GetElem(LinkList L,int i,ElemType *e) { Node *p=L->next; int j=1; while(p&&j<i){ p=p->next; j++; } if(j==i){ *e=p->data; }else{ return ERROR; } return OK; } /* 初始条件:顺序线性表L已存在 */ /* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */ /* 若这样的数据元素不存在,则返回值为0 */ int LocateElem(LinkList L,ElemType e) { int i=1; Node *p=L->next; while(p){ if(p->data==e){ return i; } i++; p=p->next; } if(!p){ return 0; } } /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L), */ /* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */ Status ListInsert(LinkList *L,int i,ElemType e) { if(i>ListLength(*L)+1){ return ERROR; } Node *p=*L; int j=1; while(p&&j<i){ p=p->next; j++; } Node *node=(Node*)malloc(sizeof(Node)); node->data=e; node->next=p->next; p->next=node; return OK; } /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */ /* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */ Status ListDelete(LinkList *L,int i,ElemType *e) { if(ListEmpty(*L)||i>ListLength(*L)){ return ERROR; } int j=1; Node *p=*L; while(p&&j<i){ p=p->next; j++; } Node *q=p->next; p->next=q->next; *e=q->data; free(q); q=NULL; return OK; } /* 初始条件:顺序线性表L已存在 */ /* 操作结果:依次对L的每个数据元素输出 */ Status ListTraverse(LinkList L) { if(ListEmpty(L)){ return ERROR; } Node *p=L->next; while(p){ visit(p->data); p=p->next; } printf("\n"); return OK; } /* 随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */ void CreateListHead(LinkList *L, int n) { //首先进行初始化 InitList(L); srand(time(0)); int i; for(i=0;i<n;i++){ Node *node=(Node*)malloc(sizeof(Node)); if(!node){ exit(OVERFLOW); } node->data=rand()%100+1; node->next=(*L)->next; (*L)->next=node; } } /* 随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法) */ void CreateListTail(LinkList *L, int n) { InitList(L); srand(time(0)); int i; Node *p=*L; for(i=0;i<n;i++){ Node *node=(Node*)malloc(sizeof(Node)); if(!node){ exit(OVERFLOW); } node->data=rand()%100+1; node->next=NULL; p->next=node; p=node; } } //下面是关于链表的一些面试题目 //已知链表的头节点,写一个函数把这个链表逆序 void ListReverse(LinkList *L){ //基本思想是使用3个指针,同时指向相邻的3个元素 if(ListEmpty(*L)){ return; } Node *p=(*L)->next; Node *q=p->next; p->next=NULL; Node *r=NULL; while(q){ r=q->next; q->next=p; p=q; q=r; } (*L)->next=p; } int main() { LinkList L; ElemType e; Status i; int j,k; i=InitList(&L); printf("初始化L后:ListLength(L)=%d\n",ListLength(L)); for(j=1;j<=5;j++) i=ListInsert(&L,1,j); printf("在L的表头依次插入1~5后:L.data="); ListTraverse(L); printf("ListLength(L)=%d \n",ListLength(L)); ListReverse(&L); printf("链表逆序之后,ListLength(L)=%d; \nL.data=",ListLength(L)); ListTraverse(L); i=ListEmpty(L); printf("L是否空:i=%d(1:是 0:否)\n",i); i=ClearList(&L); printf("清空L后:ListLength(L)=%d\n",ListLength(L)); i=ListEmpty(L); printf("L是否空:i=%d(1:是 0:否)\n",i); for(j=1;j<=10;j++) ListInsert(&L,j,j); printf("在L的表尾依次插入1~10后:L.data="); ListTraverse(L); printf("ListLength(L)=%d \n",ListLength(L)); ListInsert(&L,1,0); printf("在L的表头插入0后:L.data="); ListTraverse(L); printf("ListLength(L)=%d \n",ListLength(L)); GetElem(L,5,&e); printf("第5个元素的值为:%d\n",e); for(j=3;j<=4;j++) { k=LocateElem(L,j); if(k) printf("第%d个元素的值为%d\n",k,j); else printf("没有值为%d的元素\n",j); } k=ListLength(L); /* k为表长 */ for(j=k+1;j>=k;j--) { i=ListDelete(&L,j,&e); /* 删除第j个数据 */ if(i==ERROR) printf("删除第%d个数据失败\n",j); else printf("删除第%d个的元素值为:%d\n",j,e); } printf("依次输出L的元素:"); ListTraverse(L); j=5; ListDelete(&L,j,&e); /* 删除第5个数据 */ printf("删除第%d个的元素值为:%d\n",j,e); printf("依次输出L的元素:"); ListTraverse(L); i=ClearList(&L); printf("\n清空L后:ListLength(L)=%d\n",ListLength(L)); CreateListHead(&L,20); printf("整体创建L的元素(头插法):"); ListTraverse(L); i=ClearList(&L); printf("\n删除L后:ListLength(L)=%d\n",ListLength(L)); CreateListTail(&L,20); printf("整体创建L的元素(尾插法):"); ListTraverse(L); return 0; }
//对链表进行逆转 Status ListReverse(LinkList *L){ //含有头节点的话,第一个和最后一个都需要进行额外处理 Node *p=(*L); Node *q=(*L)->next; Node *r=NULL; while(q){ r=q->next; q->next=p; p=q; q=r; } (*L)->next=NULL; *L=p; return OK; }
B。两个有序链表的合并
/无头节点链表合并的非递归方法 LinkList mergeSortedList(LinkList L1,LinkList L2){ LinkList L=(LinkList)malloc(sizeof(Node)); L->next=NULL; Node *p1=L1; Node *p2=L2; Node *p=NULL; //没有头节点的链表,对于第一个需要额外处理 if(p1->data<=p2->data){ L=p1; p1=p1->next; }else{ L=p2; p2=p2->next; } p=L; while(p1&&p2){ if(p1->data<=p2->data){ p->next=p1; p1=p1->next; p=p->next; }else if(p1->data>p2->data){ p->next=p2; p2=p2->next; p=p->next; } } p->next=p1?p1:p2; return L; }
//采用递归的方法实现无头节点链表的合并,每次递归返回合并后新链表的头部节点 //递归中的局部变量一般是无意义的,除非该局部变量作为函数返回值返回,(如果作为下一次递归的调用参数呢?) //关于理解递归的一种方法,把它当作另外一个函数,只是样子长得一样而已 LinkList recursiveMergeSortedList(LinkList L1,LinkList L2){ //ListTraverse(L1); //ListTraverse(L2); if(!L1||!L2){ return L1?L1:L2; } Node *node; if(L1->data<=L2->data){ node=L1; node->next=recursiveMergeSortedList(L1->next,L2); }else if(L1->data>L2->data){ node=L2; node->next=recursiveMergeSortedList(L1,L2->next); } return node; }
//合并两个带有头节点的有序单链表,这种方法的漏洞是没有考虑两条链表有交集的情况 LinkList mergeSortedList(LinkList L1,LinkList L2){ LinkList L; InitList(&L); LinkList p=L; Node* pa=L1->next; Node* pb=L2->next; while(pa&&pb){ if(pa->data<=pb->data){ p->next=pa; p=pa; pa=pa->next; }else{ p->next=pb; p=pb; pb=pb->next; } } if(pa==NULL){ p->next=pb; }else if(pb=NULL){ p->next=pa; } return L; }
和不含头节点的递归实现相同,只不过在函数调用之前,蒋传入参数和返回值处理成不包含头节点的情况
(思考,这个代码是否有问题?如果两个链表有交集呢?)
C。寻找链表的中间节点(采取两个指针,一个步长为1,一个步长为2,步长为2的为空的时候,另外一个刚好走到中间)
//找出单向链表的中间节点 Node* middleNode(LinkList L){ Node *p; Node *q; p=L; q=L; while(q){ q=q->next->next; p=p->next; } return p; }
D判断链表是否有环(使用步长为1和步长为2的两个指针,如果两个指针相遇,说明有环,否则无环)
其他题目:
1,找出链表的倒数第k个元素
2,找出链表的中间元素
(一快一慢两个指针)
3,找出链表环的入口
设链头到入口的距离为a,相遇的地方距离入口为x,环的长度为r;那么有2*(a+x)=a+x+nr;所以又a=(n-1)r+r-x
1)让一快(2)一慢(1)两个指针一起走直到相遇
2)之后让两个指针一起走,步长皆为1,一个从头开始走,另外一个从相遇的地方开始走,那么再次相遇的地方即是入口。
4,判断两个链表是否相交,如果相交,找出交点
1)两个相交的链表一定要不都没有环,要么都有环
2)如果两个链表都没有环,若最后一个节点相同,那么相交;那么找出两个链表的长度m,n,短的从头走,长的从n-m处走,第一次相遇的地方即为交点
3)如果两个链表都有环,那么判断其中一个链表的入口节点在不在另外一个链表上,如果在,则相交。如果两个链表相交,可以定义任何一个链表的入口节点为交点。
#include "stdio.h" #include "stdlib.h" #include "math.h" #include "time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 /* 存储空间初始分配量 */ typedef int Status; typedef int SElemType; /* SElemType类型根据实际情况而定,这里假设为int */ /* 顺序栈结构 */ typedef struct { //下标为0的地方为栈底,top指针指向栈顶部 SElemType data[MAXSIZE]; int top; }SqStack; Status visit(SElemType c) { printf("%d ",c); return OK; } /* 构造一个空栈S */ Status InitStack(SqStack *s) { s->top=-1; return OK; } /* 把S置为空栈 */ Status ClearStack(SqStack *s) { s->top=-1; return OK; } /* 若栈S为空栈,则返回TRUE,否则返回FALSE */ Status StackEmpty(SqStack s) { if(s.top==-1){ return TRUE; }else{ return FALSE; } } /* 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR */ Status GetTop(SqStack s,SElemType *e) { if(s.top<0){ return ERROR; } if(s.top>=MAXSIZE-1){ return ERROR; } *e=s.data[s.top]; return OK; } /* 插入元素e为新的栈顶元素 */ Status Push(SqStack *s,SElemType e) { if(s->top>=MAXSIZE-1){ return ERROR; } else{ s->data[++s->top]=e; } return OK; } /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */ Status Pop(SqStack *s,SElemType *e) { if(s->top<=0){ return ERROR; } *e=s->data[s->top]; s->top--; return OK; return OK; } /* 从栈底到栈顶依次对栈中每个元素显示 */ Status StackTraverse(SqStack s) { if(s.top<=0){ return ERROR; } int i; for(i=0;i<=s.top;i++){ visit(s.data[i]); } printf("\n"); return OK; } int StackLength(SqStack s){ return ++s.top; } int main() { int j; SqStack s; int e; if(InitStack(&s)==OK) for(j=1;j<=10;j++) Push(&s,j); printf("栈中元素依次为:"); StackTraverse(s); Pop(&s,&e); printf("弹出的栈顶元素 e=%d\n",e); printf("栈空否:%d(1:空 0:否)\n",StackEmpty(s)); GetTop(s,&e); printf("栈顶元素 e=%d 栈的长度为%d\n",e,StackLength(s)); ClearStack(&s); printf("清空栈后,栈空否:%d(1:空 0:否)\n",StackEmpty(s)); return 0; }
一点总结:
1)下标为0的数组元素为栈底
2)top指针时刻指向栈顶元素
3)初始化的时候,top指针为-1
#include "stdio.h" #include "stdlib.h" #include "math.h" #include "time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 /* 存储空间初始分配量 */ typedef int Status; typedef int SElemType; /* SElemType类型根据实际情况而定,这里假设为int */ /* 链栈结构 */ typedef struct StackNode { SElemType data; //指针变量,存放的是所指向变量的地址 //&取地址运算符 //*取出其指向地址处变量的内容,而不是取出地址 struct StackNode *next; }StackNode,*LinkStackPtr; typedef struct { int count; LinkStackPtr top; }LinkStack; Status visit(SElemType c) { printf("%d ",c); return OK; } /* 构造一个空栈S */ Status InitStack(LinkStack *S) { S->count=0; S->top=NULL; return OK; } /* 把S置为空栈 */ Status ClearStack(LinkStack *S) { //需要借助两个中间变量! LinkStackPtr p=S->top; LinkStackPtr q; while(p){ q=p; p=p->next; free(q); } } /* 若栈S为空栈,则返回TRUE,否则返回FALSE */ Status StackEmpty(LinkStack S) { if(S.count==0){ return TRUE; }else{ return FALSE; } } /* 返回S的元素个数,即栈的长度 */ int StackLength(LinkStack S) { return S.count; } /* 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR */ Status GetTop(LinkStack S,SElemType *e) { if(StackEmpty(S)){ return ERROR; } *e=S.top->data; return OK; } /* 插入元素e为新的栈顶元素 */ Status Push(LinkStack *S,SElemType e) { LinkStackPtr node=(LinkStackPtr)malloc(sizeof(StackNode)); node->data=e; //头指针就是首元素,因此不是node->next=S->top_>next node->next=S->top; S->top=node; S->count++; return OK; } /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */ Status Pop(LinkStack *S,SElemType *e) { if(StackEmpty(*S)){ return ERROR; } LinkStackPtr p=S->top; *e=p->data; S->top=p->next; free(p); S->count--; return OK; } Status StackTraverse(LinkStack S) { LinkStackPtr p; p=S.top; while(p){ visit(p->data); p=p->next; } printf("\n"); return OK; } int main() { int j; LinkStack s; int e; if(InitStack(&s)==OK) for(j=1;j<=10;j++) Push(&s,j); printf("栈中元素依次为:"); StackTraverse(s); Pop(&s,&e); printf("弹出的栈顶元素 e=%d\n",e); printf("栈空否:%d(1:空 0:否)\n",StackEmpty(s)); GetTop(s,&e); printf("栈顶元素 e=%d 栈的长度为%d\n",e,StackLength(s)); ClearStack(&s); printf("清空栈后,栈空否:%d(1:空 0:否)\n",StackEmpty(s)); return 0; }
一点总结:
1)链表的头元素作为栈顶元素本身(该链表是不含头节点的单链表,也就是头指针放置具体元素)。
2)注意栈的清空、插入和删除操作。
#include "stdio.h" #include "stdlib.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 /* 存储空间初始分配量 */ typedef int Status; typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */ /* 循环队列的顺序存储结构 */ typedef struct { //为了区分队列为空和队列满两种情况,队列为空的时候rear==front;队列满的时候,保留一个元素空间,也就是(rear+1)%MAXSIZE=front; QElemType data[MAXSIZE]; //通过%操作,front 和rear的取值还是始终在0和MAXSIZE之间 //front指向队列头部元素 int front; //rear指针指向对尾元素的下一个 int rear; }SqQueue; Status visit(QElemType c) { printf("%d ",c); return OK; } /* 初始化一个空队列Q */ Status InitQueue(SqQueue *Q) { Q->front=Q->rear=0; return OK; } /* 将Q清为空队列 */ Status ClearQueue(SqQueue *Q) { Q->front=Q->rear=0; return OK; } /* 若队列Q为空队列,则返回TRUE,否则返回FALSE */ Status QueueEmpty(SqQueue Q) { return Q.front==Q.rear; } Status QueueFull(SqQueue Q){ if((Q.rear+1)%MAXSIZE==Q.front){ return TRUE; }else{ return FALSE; } } /* 返回Q的元素个数,也就是队列的当前长度 */ int QueueLength(SqQueue Q) { return (Q.rear-Q.front+MAXSIZE)%MAXSIZE; } /* 若队列不空,则用e返回Q的队头元素,并返回OK,否则返回ERROR */ Status GetHead(SqQueue Q,QElemType *e) { if(QueueEmpty(Q)){ return ERROR; } *e=Q.data[Q.front]; return OK; } /* 若队列未满,则插入元素e为Q新的队尾元素 */ Status EnQueue(SqQueue *Q,QElemType e) { if(QueueFull(*Q)){ return ERROR; } Q->data[Q->rear]=e; Q->rear=(Q->rear+1)%MAXSIZE; return OK; } /* 若队列不空,则删除Q中队头元素,用e返回其值 */ Status DeQueue(SqQueue *Q,QElemType *e) { if(QueueEmpty(*Q)){ return ERROR; } *e=Q->data[Q->front]; Q->front=(Q->front+1)%MAXSIZE; return OK; } /* 从队头到队尾依次对队列Q中每个元素输出 */ Status QueueTraverse(SqQueue Q) { if(QueueEmpty(Q)){ return ERROR; } int i; for(i=Q.front;i<Q.rear;i++){ visit(Q.data[Q.data[i]]); } return OK; } int main() { Status j; int i=0,l; QElemType d; SqQueue Q; InitQueue(&Q); printf("初始化队列后,队列空否?%u(1:空 0:否)\n",QueueEmpty(Q)); printf("请输入整型队列元素(不超过%d个),-1为提前结束符: ",MAXSIZE-1); do { /* scanf("%d",&d); */ d=i+100; if(d==-1) break; i++; EnQueue(&Q,d); }while(i<MAXSIZE-1); printf("队列长度为: %d\n",QueueLength(Q)); printf("现在队列空否?%u(1:空 0:否)\n",QueueEmpty(Q)); printf("连续%d次由队头删除元素,队尾插入元素:\n",MAXSIZE); for(l=1;l<=MAXSIZE;l++) { DeQueue(&Q,&d); printf("删除的元素是%d,插入的元素:%d \n",d,l+1000); /* scanf("%d",&d); */ d=l+1000; EnQueue(&Q,d); } l=QueueLength(Q); printf("现在队列中的元素为: \n"); QueueTraverse(Q); printf("共向队尾插入了%d个元素\n",i+MAXSIZE); if(l-2>0) printf("现在由队头删除%d个元素:\n",l-2); while(QueueLength(Q)>2) { DeQueue(&Q,&d); printf("删除的元素值为%d\n",d); } j=GetHead(Q,&d); if(j) printf("现在队头元素为: %d\n",d); ClearQueue(&Q); printf("清空队列后, 队列空否?%u(1:空 0:否)\n",QueueEmpty(Q)); return 0; }
1,队头指针指向队头元素本身
2,对尾指针指向对尾元素的后一个,队头和队尾的取值还是再0-MAXSIZE之间。
3,队列为空的标志为Q->front==Q->rear
4,为了防止队列满时候和队列为空的时候,都存在Q->front==Q->rear;规定队列满的时候,保留一个队列空间不放元素,因此队列满的标志为:(Q->rear+1)%MAXSIZE==Q->front;
5,队列长度为(Q->rear-Q->front+MAXSIZE)%MAXSIZE;
#include "stdio.h" #include "stdlib.h" #include "math.h" #include "time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 /* 存储空间初始分配量 */ typedef int Status; typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */ typedef struct QNode /* 结点结构 */ { QElemType data; struct QNode * next; }QNode,*QueuePtr; typedef struct /* 队列的链表结构 */ { //队头指针指向头节点,并不存放具体元素,哪怕队列为空,也有一个队头指针 QueuePtr front; //队尾指针指向队尾元素本身 QueuePtr rear; }LinkQueue; Status visit(QElemType c) { printf("%d ",c); } /* 构造一个空队列Q */ Status InitQueue(LinkQueue *Q) { QueuePtr node=(QueuePtr)malloc(sizeof(QNode)); node->next=NULL; Q->rear=Q->front=node; return OK; } /* 销毁队列Q */ Status DestroyQueue(LinkQueue *Q) { QueuePtr p=Q->front; QueuePtr q=NULL; while(p){ q=p->next; free(p); p=q; } return OK; } /* 将Q清为空队列 */ Status ClearQueue(LinkQueue *Q) { //只是蒋数据清空,但是保留头指针,也就是队头 QueuePtr p=Q->front->next; QueuePtr q=NULL; while(p){ q=p->next; free(p); p=q; } Q->front->next=NULL; Q->rear=Q->front; } /* 若Q为空队列,则返回TRUE,否则返回FALSE */ Status QueueEmpty(LinkQueue Q) { if(Q.rear==Q.front){ return TRUE; }else{ return FALSE; } } /* 求队列的长度 */ int QueueLength(LinkQueue Q) { int i=0; QueuePtr q=Q.front; while(q!=Q.rear){ i++; q=q->next; } return i; } /* 若队列不空,则用e返回Q的队头元素,并返回OK,否则返回ERROR */ Status GetHead(LinkQueue Q,QElemType *e) { if(QueueEmpty(Q)){ return ERROR; } *e=Q.front->next->data; return OK; } /* 插入元素e为Q的新的队尾元素 */ Status EnQueue(LinkQueue *Q,QElemType e) { QueuePtr node=(QueuePtr)malloc(sizeof(QNode)); node->data=e; node->next=NULL; Q->rear->next=node; Q->rear=node; return OK; } /* 若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR */ Status DeQueue(LinkQueue *Q,QElemType *e) { if(QueueEmpty(*Q)){ return ERROR; } //删除的不是头指针,头指针并不放元素,而是头指针的下一个元素 QueuePtr p=Q->front->next; *e=p->data; Q->front->next=p->next; //如果要删除的恰好就是尾元素,蒋队列重新置为初始状态 if(p==Q->rear){ Q->rear=Q->front; Q->front->next=NULL; } free(p); p=NULL; } /* 从队头到队尾依次对队列Q中每个元素输出 */ Status QueueTraverse(LinkQueue Q) { QueuePtr q; q=Q.front->next; while(q){ visit(q->data); q=q->next; } printf("\n"); } int main() { int i; QElemType d; LinkQueue q; i=InitQueue(&q); if(i) printf("成功地构造了一个空队列!\n"); printf("是否空队列?%d(1:空 0:否) ",QueueEmpty(q)); printf("队列的长度为%d\n",QueueLength(q)); EnQueue(&q,-5); EnQueue(&q,5); EnQueue(&q,10); printf("插入3个元素(-5,5,10)后,队列的长度为%d\n",QueueLength(q)); printf("是否空队列?%d(1:空 0:否) ",QueueEmpty(q)); printf("队列的元素依次为:"); QueueTraverse(q); i=GetHead(q,&d); if(i==OK) printf("队头元素是:%d\n",d); DeQueue(&q,&d); printf("删除了队头元素%d\n",d); i=GetHead(q,&d); if(i==OK) printf("新的队头元素是:%d\n",d); ClearQueue(&q); printf("清空队列后,q.front=%u q.rear=%u q.front->next=%u\n",q.front,q.rear,q.front->next); DestroyQueue(&q); printf("销毁队列后,q.front=%u q.rear=%u\n",q.front, q.rear); return 0; }
1,队列所在链表的头指针不放置元素,也就是包含头节点
2,队列的front指针即为链表的头指针,即队头部元素的前一个
3,队列的rear指针指向尾元素本身
4,队列为空的标志为:q->front=q->rear。因此清空队列的时候,对于最后一个元素需要额外判断。
5,注意队列的初始化,销毁和置空的区别。
二叉树
二叉树具有以下性质:
1.深度为k的二叉树至多有2的k次方-1个节点
2.第k层之多有2的(k-1)个元素
3.n0=n2+1
4,具有n个节点的完全二叉树的深度为(log2(n)+1)
5,对于完全二叉树,如果从0开始编号,那么i的根节点为i/2;左孩子为2i+1,右孩子为2i+2
#include "stdio.h" #include "stdlib.h" #include <math.h> #include "time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 100 /* 存储空间初始分配量 */ #define MAX_TREE_SIZE 100 /* 二叉树的最大结点数 */ typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */ typedef int TElemType; /* 树结点的数据类型,目前暂定为整型 */ TElemType Nil=0; typedef TElemType SqBiTree[MAX_TREE_SIZE]; /* 0号单元存储根结点 */ typedef struct { int level; int order; }Position; Status visit(TElemType c) { printf("%d ",c); return OK; } /* 构造空二叉树T。因为T是固定数组,不会改变,故不需要& */ Status InitBiTree(SqBiTree T) { int i; for(i=0;i<MAXSIZE;i++){ T[i]=Nil; } return OK; } Status CreateBiTree(SqBiTree T){ //注意双亲节点不能为空 int i; for(i=0;i<=10;i++){ T[i]=i+1; if(T[i]!=Nil&&T[(int)(i-1)/2]==Nil){ printf("双亲节点不能为空!"); exit(ERROR); } } for(i=11;i<MAXSIZE;i++){ T[i]=Nil; } return OK; } Status BiTreeEmpty(SqBiTree T){ //如果根节点为空,那么整个树为空 if(T[0]==Nil){ return TRUE; }else{ return FALSE; } } //计算二叉树的深度也就是有多少层 int BiTreeDepth(SqBiTree T) { if(BiTreeEmpty(T)){ return 0; } int i; for(i=MAXSIZE-1;i>=0;i--){ if(T[i]!=Nil){ break; } } return (int)(log(i)/log(2)); } /* 初始条件: 二叉树T存在 */ /* 操作结果: 当T不空,用e返回T的根,返回OK;否则返回ERROR,e无定义 */ Status Root(SqBiTree T,TElemType *e) { if(BiTreeEmpty(T)){ return ERROR; } *e=T[0]; return OK; } /* 初始条件: 二叉树T存在,e是T中某个结点(的位置) */ /* 操作结果: 返回处于位置e(层,本层序号)的结点的值 */ TElemType Value(SqBiTree T,Position e) { if(BiTreeEmpty(T)){ return ERROR; } return T[(int)pow(2,e.level-1)+e.order-2]; } /* 初始条件: 二叉树T存在,e是T中某个结点(的位置) */ /* 操作结果: 给处于位置e(层,本层序号)的结点赋新值value */ Status Assign(SqBiTree T,Position e,TElemType value) { if(BiTreeEmpty(T)){ return ERROR; } int index=(int)pow(2,e.level-1)+e.order-2; if(value!=Nil&&T[(index-1)/2]==Nil){ return ERROR; } if(value==Nil&&(T[index*2+1]!=Nil||T[index*2+2]!=Nil)){ return ERROR; } T[index]=value; return OK; } Status PreTraverse(SqBiTree T,int e){ visit(T[e]); if(T[2*e+1]!=Nil){ PreTraverse(T,2*e+1); } if(T[2*e+2]!=Nil){ PreTraverse(T,2*e+2); } } Status PreOrderTraverse(SqBiTree T) { if(!BiTreeEmpty(T)){ PreTraverse(T,0); } printf("\n"); return OK; } /* InOrderTraverse()调用 */ void InTraverse(SqBiTree T,int e) { if(T[2*e+1]!=Nil){ InTraverse(T,2*e+1); } visit(T[e]); if(T[2*e+2]!=Nil){ InTraverse(T,2*e+2); } } /* 初始条件: 二叉树存在 */ /* 操作结果: 中序遍历T。 */ Status InOrderTraverse(SqBiTree T) { if(!BiTreeEmpty(T)){ InTraverse(T,0); } printf("\n"); return OK; } /* PostOrderTraverse()调用 */ void PostTraverse(SqBiTree T,int e) { if(T[2*e+1]!=Nil){ PostTraverse(T,2*e+1); } if(T[2*e+2]!=Nil){ PostTraverse(T,2*e+2); } visit(T[e]); } /* 初始条件: 二叉树T存在 */ /* 操作结果: 后序遍历T。 */ Status PostOrderTraverse(SqBiTree T) { if(!BiTreeEmpty(T)){ PostTraverse(T,0); } return OK; } /* 层序遍历二叉树 */ void LevelOrderTraverse(SqBiTree T) { //按照下标的顺序遍历即可 int j; for(j=MAX_TREE_SIZE-1;T[j]==Nil;j--); int i; for(i=0;i<=j;i++){ visit(T[i]); } printf("\n"); } /* 逐层、按本层序号输出二叉树 */ void Print(SqBiTree T) { int i,j,k; Position p; TElemType e; for(j=1;j<=BiTreeDepth(T);j++){ printf("第%d层:",j); for(k=1;k<=pow(2,j-1);k++){ e=Value(T,p); printf("第%d个:%d,",k,e); } printf("\n"); } } Status ClearBiTree(SqBiTree T){ int i; for(i=0;i<MAX_TREE_SIZE;i++){ T[i]=Nil; } return OK; } int main() { Status i; Position p; TElemType e; SqBiTree T; InitBiTree(T); CreateBiTree(T); printf("建立二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T)); i=Root(T,&e); if(i) printf("二叉树的根为:%d\n",e); else printf("树空,无根\n"); printf("层序遍历二叉树:\n"); LevelOrderTraverse(T); printf("前序遍历二叉树:\n"); PreOrderTraverse(T); printf("中序遍历二叉树:\n"); InOrderTraverse(T); printf("后序遍历二叉树:\n"); PostOrderTraverse(T); printf("修改结点的层号3本层序号2。"); p.level=3; p.order=2; e=Value(T,p); printf("待修改结点的原值为%d请输入新值:50 ",e); e=50; Assign(T,p,e); printf("前序遍历二叉树:\n"); PreOrderTraverse(T); ClearBiTree(T); printf("清除二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T)); i=Root(T,&e); if(i) printf("二叉树的根为:%d\n",e); else printf("树空,无根\n"); return 0; }
1)顺序实现中就是利用下标来代表父母节点、子节点、兄弟节点之间的关系。以及二叉树的性质和坐标之间的关系罢了。
2)在赋值时需要注意一条约束,如果子节点不为空,那么父母节点一定不能为空。
3)重点关注先序、中序、后序遍历的实现。
#include "string.h" #include "stdio.h" #include "stdlib.h" #include "math.h" #include "time.h" #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 100 /* 存储空间初始分配量 */ typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */ /* 用于构造二叉树********************************** */ typedef char String[24]; /* 0号单元存放串的长度 */ String str; Status StrAssign(String T,char *chars) { int i; if(strlen(chars)>MAXSIZE){ return ERROR; } T[0]=strlen(chars); for(i=1;i<=T[0];i++){ T[i]=*(chars+i-1); } return OK; } /* ************************************************ */ typedef char TElemType; TElemType Nil=' '; /* 字符型以空格符为空 */ Status visit(TElemType e) { printf("%c ",e); return OK; } //采用左右孩子表示方法 typedef struct BiTNode /* 结点结构 */ { TElemType data; struct BiTNode *lchild,*rchild; }BiTNode,*BiTree; /* 构造空二叉树T */ Status InitBiTree(BiTree *T) { (*T)=NULL; return; } /* 初始条件: 二叉树T存在。操作结果: 销毁二叉树T */ //利用递归的方法进行销毁 void DestroyBiTree(BiTree *T) { if(!(*T)){ return; } if((*T)->lchild){ DestroyBiTree(&((*T)->lchild)); } if((*T)->rchild){ DestroyBiTree(&((*T)->rchild)); } free(*T); *T=NULL; } int myIndex=1; /* 按前序输入二叉树中结点的值(一个字符) */ /* #表示空树,构造二叉链表表示二叉树T。 */ void CreateBiTree(BiTree *T) { TElemType ch; ch=str[myIndex++]; if(ch=='#'){ *T=NULL; }else{ (*T)=(BiTNode*)malloc(sizeof(BiTNode)); if(!(*T)){ exit(OVERFLOW); } (*T)->data=ch; CreateBiTree(&((*T)->lchild)); CreateBiTree(&((*T)->rchild)); } } /* 初始条件: 二叉树T存在 */ /* 操作结果: 若T为空二叉树,则返回TRUE,否则FALSE */ Status BiTreeEmpty(BiTree T) { if(T) return FALSE; else return TRUE; } #define ClearBiTree DestroyBiTree /* 初始条件: 二叉树T存在。操作结果: 返回T的深度 */ int BiTreeDepth(BiTree T) { int i,j; if(!T){ return 0; } if(T->lchild){ i=BiTreeDepth(T->lchild); }else{ i=0; } if(T->rchild){ j=BiTreeDepth(T->rchild); }else{ j=0; } // printf("%d %d\n",i,j); return i>j?i+1:j+1; } /* 初始条件: 二叉树T存在。操作结果: 返回T的根 */ TElemType Root(BiTree T) { if(BiTreeEmpty(T)){ return Nil; } return T->data; } /* 初始条件: 二叉树T存在,p指向T中某个结点 */ /* 操作结果: 返回p所指结点的值 */ TElemType Value(BiTree p) { return p->data; } /* 给p所指结点赋值为value */ void Assign(BiTree p,TElemType value) { p->data=value; } /* 初始条件: 二叉树T存在 */ /* 操作结果: 前序递归遍历T */ void PreOrderTraverse(BiTree T) { if(T==NULL){ return; } visit(T->data); if(T->lchild){ PreOrderTraverse(T->lchild); } if(T->rchild){ PreOrderTraverse(T->rchild); } } /* 初始条件: 二叉树T存在 */ /* 操作结果: 中序递归遍历T */ void InOrderTraverse(BiTree T) { if(!T){ return ; } if(T->lchild){ InOrderTraverse(T->lchild); } visit(T->data); if(T->rchild){ InOrderTraverse(T->rchild); } } /* 初始条件: 二叉树T存在 */ /* 操作结果: 后序递归遍历T */ void PostOrderTraverse(BiTree T) { if(!T){ return; } if(T->lchild){ PostOrderTraverse(T->lchild); } if(T->rchild){ PostOrderTraverse(T->rchild); } visit(T->data); } int main() { int i; BiTree T; TElemType e1; InitBiTree(&T); StrAssign(str,"ABDH#K###E##CFI###G#J##"); CreateBiTree(&T); printf("构造空二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T)); e1=Root(T); printf("二叉树的根为: %c\n",e1); printf("\n前序遍历二叉树:"); PreOrderTraverse(T); printf("\n中序遍历二叉树:"); InOrderTraverse(T); printf("\n后序遍历二叉树:"); PostOrderTraverse(T); ClearBiTree(&T); printf("\n清除二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T)); i=Root(T); if(!i) printf("树空,无根\n"); return 0; }
1)空指针、指针变量为空、指向指针的指针这其中有些概念还是不清楚。
2)二叉树的销毁、创建、求树深、遍历都是采用递归方法,注意递归的原理和。
关于二叉树的经典题目;
1)判定两颗二叉树是否相等
bool bitreeEqual(Node* node1,Node* node2){ if(node1==NULL&&node2==NULL){ return true; } if(node1!=NULL||node2!=NULL){ return false; } if(node1->data==node2->data){ return bitreeEqual(node1->left,node2->left)&&bitreeEqual(node1->right,node2->right); } }
2)如果二叉树的左右节点可以旋转,判断其是否相等
思路同上道题,最后的判定条件修改即可
3)计算二叉树的高度
//计算二叉树的深度 int bitreeDepth(Node* node){ if(node==NULL){ return 0; } int lDepth=bitreeDepth(node->left)+1; int rDepth=bitreeDepth(node->right)+1; return lDepth>rDepth?lDepth:rDepth; }
4)计算二叉树两个节点间的最大路径长度
5)判断一棵树是不是二叉查找树
bool isSearchTree(Node* node){ if(node==NULL){ return true; } if(node->left==NULL&&node->right==NULL){ return true; } if(node->left==NULL&&node->right!=NULL){ if(node->right->data>=node->data){ return isSearchTree(node->right); }else{ return false; } } if(node->right==NULL&&node->left!=NULL){ if(node->left->data<=node->data){ return isSearchTree(node->left); }else{ return false; } }
思路二:二叉查找树的中序遍历是递增序列,也可以从这个角度出发
6) 判断一棵树是不是AVL树
7)AVL树木的插入和删除
以上都是采用递归的思路解决:书写递归是的一些总结:
1)首先构造递归到终点的极限情况,如节点为空,下标超范等
2)其次是先序,后序还是中序,具体问题具体分析
3)上一次的递归结果,作为这一次的条件依赖(比如两棵树是否相等)还是输入(比如计算树的高度),具体问题具体分析
4)递归之间传递参数,可以有的方法是:1)返回值;2)参数(递归函数内部定义局部变量,并将该局部变量作为递归函数的形参);3)全局变量;4)函数形参(外部定义一个变量,并将该变量作为递归函数的形参传入),这种方式在很多情况也可以用方法2)来解决;