目录
一、用队列实现栈
--------默认可以使用队列结构和方法的实现--------- typedef struct { Queue q1; Queue q2; }MyStack; //初始化 MyStack * myStackCreate() { MyStack * pst = (MyStack *)malloc(sizeof(MyStack)); QueueInit(&pst->q1); QueueInit(%pst->q2); return pst; } void myStackPush(MyStack * obj , int x) { //往不为空的队列中插入数据 if(!QueueEmpty(&obj->q1)) { QueuePush(&obj->q1,x); } else { QueuePush(&obj->q2,x); } } //将不为空队列中前size-1个数据挪到另一个队列中 //再将最后一个数据出队列 int myStackPop(MyStack * obj) { QUeue * emp = &obj->q1; Queue * noeEmp = &obj->q2; if(QUeueEmpty(&obj->q2)) { emp = &obj->q2; oneEmp = &obj->q1; } while(QueueSize(oneEmp) > 1) { QueuePush(emp,QueueFront(noneEmp)); QueuePop(noneEmp); } int top = QueueFront(noneEmp); QueuePop(noneEmp); return top; } //取栈顶 int myStackTop(MyStack * obj) { if(!QueueEmpty(&obj->q1)) { return QueueBack(&obj->q1); } else { return QueueBack(&ovj->q2); } } bool myStackEmpty(MyStack * obj) { return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2); } //销毁 void myStackFree(MyStack * obj) { QueueDestory(&obj->q1); QueueDestory(&obj->q2); free(obj); obj = NULL; }
二、用栈实现队列
![]()
![]()
-----默认可以使用栈结构和方法的实现----- typedef struct { ST pushST; ST popST; }MyQueue; MyQueue * myQueueCreate { MyQueue * pq = (MyQueue *)malloc(sizeof(MyQueue)); StackInit(&pq->pushST); StackInit(&pa->popST); return pq; } void myQUeuePush(MyQueue * obj, int x) { //往pushST中插入数据 StackPush(&obj->pushST,x); } //检查popST是否为空 //不为空,直接出popST栈顶 //为空,pushST数据导入到popST中,再出popST栈顶 int myQueuePop(MyQueue * obj) { if(StackEmpty(&obj->popST)) { //导数据 while(!StackEmpty(&obj->popST)) { StackPush(&obj->popST,StackTop(&obj->pushST)); Stackpop(&obj->pushST); } } return Stack(&obj->popST); } bool myQUeueEmpty(MyQueue * obj) { return StackEmpty(&obj->pushST) && StackEmpty(&obj->popST); } void myQueueFree(MyQueue * obj) { StackDestory(&obj->pushST); StackDestory(&obj->pushST); free(obj); obj = NULL; }
三、树
1、概念
树是⼀种非线性的数据结构,它是由 n(n>=0) 个有限结点组成⼀个具有层次关系的集合。把它叫做树是因为它看起来像⼀棵倒挂的树,也就是说它是根朝上,而叶朝下的。
有⼀个特殊的结点,称为根结点,根结点没有前驱结点。
除根结点外,其余结点被分成 M(M>0) 个互不相交的集合 T1、T2、......、Tm ,其中每⼀个集合 Ti(1 <= i <= m) 又是⼀棵结构与树类似的子树。每棵子树的根结点有且只有⼀个前驱,可以 有 0 个或多个后继。因此,树是递归定义的。
树形结构中,子树之间不能有交集,否则就不是树形结构
非树形结构:
子树是不相交
除了根结点外,每个结点有且仅有⼀个父结点
⼀棵N个结点的树有N-1条边
⽗结点/双亲结点:若⼀个结点含有⼦结点,则这个结点称为其⼦结点的⽗结点; 如上图:A是B的⽗结点
⼦结点/孩⼦结点:⼀个结点含有的⼦树的根结点称为该结点的⼦结点; 如上图:B是A的孩⼦结点 结点的度:⼀个结点有⼏个孩⼦,他的度就是多少;⽐如A的度为6,F的度为2,K的度为0
树的度:⼀棵树中,最⼤的结点的度称为树的度; 如上图:树的度为 6
叶⼦结点/终端结点:度为 0 的结点称为叶结点; 如上图: B、C、H、I... 等结点为叶结点 分⽀结点/⾮终端结点:度不为 0 的结点; 如上图: D、E、F、G... 等结点为分⽀结点
兄弟结点:具有相同⽗结点的结点互称为兄弟结点(亲兄弟); 如上图: B、C 是兄弟结点
结点的层次:从根开始定义起,根为第 1 层,根的⼦结点为第 2 层,以此类推;
树的⾼度或深度:树中结点的最⼤层次; 如上图:树的⾼度为 4
结点的祖先:从根到该结点所经分⽀上的所有结点;如上图: A 是所有结点的祖先
路径:⼀条从树中任意节点出发,沿⽗节点-⼦节点连接,达到任意节点的序列;⽐如A到Q的路径为: A-E-J-Q;H到Q的路径H-D-A-E-J-Q
⼦孙:以某结点为根的⼦树中任⼀结点都称为该结点的⼦孙。如上图:所有结点都是A的⼦孙
森林:由 m(m>0) 棵互不相交的树的集合称为森林
四、二叉树
1、概念
在树形结构中,我们最常⽤的就是⼆叉树,⼀棵⼆叉树是结点的⼀个有限集合,该集合由⼀个根结点 加上两棵别称为左⼦树和右⼦树的⼆叉树组成或者为空。
二叉树的特点:
⼆叉树不存在度⼤于 2 的结点
⼆叉树的⼦树有左右之分,次序不能颠倒,因此⼆叉树是有序树
注意:对于任意的⼆叉树都是由以下⼏种情况复合⽽成的
2、满二叉树
⼀个⼆叉树,如果每⼀个层的结点数都达到最⼤值,则这个⼆叉树就是满⼆叉树。也就是说,如果⼀ 个⼆叉树的层数为 K ,且结点总数是 2 k − 1 ,则它就是满⼆叉树。
满二叉树的特点:
若规定根结点的层数为 1 ,则⼀棵⾮空⼆叉树的第i层上最多有 2 i−1 个结点若规定根结点的层数为 1 ,则深度为 h 的⼆叉树的最⼤结点数是 2 − h 1 满二叉树 完全二叉树
若规定根结点的层数为 1 ,具有 n 个结点的满⼆叉树的深度 ( log 以2为底, n+1 为对数) h = log
3、完全二叉树
完全⼆叉树是效率很⾼的数据结构,完全⼆叉树是由满⼆叉树⽽引出来的。对于深度为 K 的,有 n 个 结点的⼆叉树,当且仅当其每⼀个结点都与深度为K的满⼆叉树中编号从 1 ⾄ n 的结点⼀ 对应时称 之为完全⼆叉树。要注意的是满⼆叉树是⼀种特殊的完全⼆叉树。
若规定根结点的层数为 1 ,则⼀棵⾮空⼆叉树的第i层上最多有 2 i−1 个结点
2)若规定根结点的层数为 1 ,则深度为 h 的⼆叉树的最⼤结点数是 2 − h 1 满二叉树 完全二叉树
3)若规定根结点的层数为 1 ,具有 n 个结点的满⼆叉树的深度 ( log 以2为底, n+1 为对数) h = log2 (n + 1)
除了最后一层的结点个数一定达到最大,最后一层结点个数不一定达到最大
4、存储结构
⼆叉树⼀般可以使⽤两种结构存储,⼀种顺序结构,⼀种链式结构。
顺序结构:顺序结构存储就是使⽤数组来存储,⼀般使⽤数组只适合表⽰完全⼆叉树,因为不是完全⼆叉树会有 空间的浪费,完全⼆叉树更适合使⽤顺序结构存储。
现实中我们通常把堆(⼀种⼆叉树)使⽤顺序结构的数组来存储,需要注意的是这⾥的堆和操作系统 虚拟进程地址空间中的堆是两回事,⼀个是数据结构,⼀个是操作系统中管理内存的⼀块区域分段。
链式结构:⼆叉树的链式存储结构是指,⽤链表来表⽰⼀棵⼆叉树,即⽤链来指⽰元素的逻辑关系。 通常的⽅法 是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别⽤来给出该结点左孩⼦和右孩⼦所在的链结点的存储地址 。链式结构⼜分为⼆叉链和三叉链,当前我们学习中⼀般都是⼆叉链。后 ⾯课程学到⾼阶数据结构如红⿊树等会⽤到三叉链。
五、堆
⼀般堆使⽤顺序结构的数组来存储数据,堆是⼀种特殊的⼆叉树,具有⼆叉树的特性的同时,还具备 其他的特性。
1、概念
如果有⼀个关键码的集合K = {k0 , k1 , k2 , ...,kn−1 } ,把它的所有元素按完全⼆叉树的顺序存储⽅ 式存储,在⼀个⼀维数组中,并满⾜: Ki = K2∗i+1 (Ki >= K2∗i+1且Ki <= K2∗i+2 ), i = 0、1、2... ,则称为⼩堆(或⼤堆)。将根结点最⼤的堆叫做最⼤堆或⼤根堆,根结点最⼩的堆 叫做最⼩堆或⼩根堆。
堆具有以下性质:
堆中某个结点的值总是不⼤于或不⼩于其⽗结点的值;
堆总是⼀棵完全⼆叉树。
⼆叉树性质
对于具有 n 个结点的完全⼆叉树,如果按照从上⾄下从左⾄右的数组顺序对所有结点从 0 开始编号,则对于序号为 i 的结点有:
1. 若 i>0 , i 位置结点的双亲序号: (i-1)/2 ; i=0 , i 为根结点编号,⽆双亲结点
2. 若 2i+1=n 否则⽆左孩⼦
3. 若 2i+2=n 否则⽆右孩⼦
2、相关算法实现
//堆的结构 typedef int HPDataType; typedef struct Heap { HPDataType * arr; int size; //有效数据个数 int capacity; //空间大小 }HP; 堆的销毁 void HPDestroy(HP * php) { if(php->arr) { free(php->arr); } php->arr = NULL; php->size = php->capacity = 0; } //打印堆 void HPPrint(HP * php) { for(int i = 0; i < php->size;i++) { printf("%d ",php->arr[i]); } printf("\n"); } //交换 void Swap(int * x, int * y) { int tmp = *x; *x = *y; *y = tmp; } //堆判空 bool HPEmpty(HP * php) { assert(php); return php->size == 0; } //向上调整算法 void AdjustUp(HPDataType * arr,int child) { int parent = (child - 1)/2; while() { //大堆:> //小堆:< if(arr[child] < arr[parent]) { //调整 Swap(&arr[chlid],&arr[parent]); child = parent; parent = (child-1) / 2; } else { break; } } } //向下调整算法 void AdjustDown(HPDataType * arr, int parent, int n) { //左孩子 int child = parent * 2 + 1; while(child < n) { //大堆:< //小堆:> if(child + 1 < n && arr[child] < arr[chlid + 1]) { child++; } //大堆:> //小堆:< if(arr[child] > arr[parent]) { //调整 Swap(&arr[child],&arr[parent]); parent = child; child = parent * 2 + 1; } else { break; } } } void HPPush(HP * php , HPDataType x) { assert(php); //判断空间是否足够 if(php->size == php->capacity) { int newCapcity = php->capacity == 0 ? 4 : 2 * php->capacity; HPDataType & tmp = (HPDataType *)realloc(php->arr,newCapacity * sizeof(HPDataType)); if(tmp == NULL) { perror("realloc fail"); exit(1); } php->arr = tmp; php->capacity = newCapcity; } php->arr[php->size++] = x; //向上调整 AdjustUp(php->arr,php->size); ++php->size; } //入堆时间复杂度O(logn) void HPPop(HP * php) { assert(!HPEmpty(php)); // 0 php->size-1 Swap(&php->aee[0],&php->arr[php->size-1]); --php->size; //向下调整 AdjustDown(php->arr,0,php->size); } //出堆的时间复杂度O(logn) //向堆内插入数据时向上调整算法指定堆是大堆,那么出堆和入堆操作就要指定大堆 //取堆顶数据 HPDataType HPTop(HP * php) { assert(!HPEmpty(php)); return php->arr[0]; } //堆排序 vodi HeapSort(int * arr, int n) { //借助数据结构堆来实现堆排序 Hp hp; HPInit(&hp); for(int i = 0;i < n;i++) { HPPush(&hp,arr[i]); } int i = 0; while(!HPEmpty(&hp)) { int top = HPTop(&hp); arr[i++] = top; printf("%d ",top); HPPop(&hp); } HPDestroy(&hp); }堆排序的思想时借助堆的思想来排序
那如何将数组本身来建堆?
根据数组来进行建堆 - 排升序建大堆,排降序小堆
将堆顶和最后一个数据交换位置, -- size , 向下调整堆,继续重复该过程
//向下调整建堆 - 建大堆 void HeapSort(int * arr,int n) { //建堆 - 向下调整算法建堆 for(int i = (n-1-1) / 2;i >= 0;i--) { AdjustDown(arr,i,n); } //堆排序 int end = n-1; while(end > 0) { Swap(&arr[0],&arr[end]); AdjustDown(arr,0,end); end--; } } void arrPrint(int * arr,int n) { for(int i = 0;i < n;i++) { printf("%d ",arr[i]); } printf("\n"); }











