二叉树
树形的最简单的模型,每个节点最多有两个子节点
一般来说,非二叉树,可以简化成二叉树
每个子节点有且仅有一个父节点,整棵树只有一个根节点
具有递归的结构特征,用递归的方法处理,可以简化算法
三种遍历序:
前序遍历 D-L-R
中序遍历 L-D-R
后序遍历 L-R-D
比如:1是根节点 2是左子节点 3是右子节点
前序遍历 1 2 3
中序遍历 2 1 3
后序遍历 2 3 1
二叉树的一般形式:
根节点、枝节点、叶节点
父节点和子节点
左子节点和右子节点
左子树和右子树
大小和高度(深度)
满二叉树和完全二叉树
满二叉树:
每层节点数均达到最大值
所有枝节点均有左右子树
完全二叉树:
前面n-1 层是满的,最后1层可以不满,
但是从左向右连续排列
平衡二叉树:
左右层数不能相差1的;
二叉树的编码实现:
顺序二叉树 -----数组只能还原完全二叉树,所以不是完全二叉树的 需要变成完全二叉树
最大的问题 就是如果二叉树离完全二叉树标准特别远,内存的浪费非常大。
因此二叉树虽然可以用 顺序结构实现,但更常见的用链表实现。
链式表二叉树
每个节点由 数据、左子节点和右子节点组成(二叉链表)。也可以加上父节点指针(三叉链表)。
二叉链表实现的二叉树 单向的; 三叉链表实现的是二叉树是 双向的。
struct BsTreeNode{
int data;//数据
struct BsTreeNode* left;//左子节点,也可以代表左边的子树
struct BsTreeNode* right;//右子节点,也可以代表右边的子树
};
编码实现 有序二叉树:
有序二叉树的原理:
首先有一个根节点(第一个节点),放入第二个节点的时候,和根节点比较,如果比根节点大,放
在根节点的右边,如果小则放在左边。(相等的话,看情况处理)。
左右子树亦分别为有序二叉树。
基于有序二叉树的排序和查找,可获得O(logN)级的平均时间复杂度
回顾:
二叉树,最简单的树型结构,每个父节点最多有两个子节点
二叉树具备递归的特征,所以编码时会大量使用递归。
二叉树有顺序结构和链式结构两种实现方式,顺序结构的实现方式必须存储完全二叉树,
不是完全二叉树的用空节点补成二叉树以后存储,因此内存上浪费比较大。
链式结构的二叉树分为二叉链表实现和三叉链表实现,三叉链表多了父节点的指针。
二叉树的遍历分为三种:前序、中序、后序
算法:
数据结构 存一起
算法 算一下
广义上说,算法就是解决问题的方法。(说到底是数学问题)
排序算法:----无序变成有序
冒泡、选择、插入、快速、归并
查找算法:----查找数据
线性查找(从头到尾查找)二分查找(有序,从中间开始查找)
冒泡排序:排序有升序和降序,升序就是从小到大,降序就是从大到小。
算法:
相邻的比较,如果前面比后面大,交换。从第一对比到最后一对,最大就在最后面。
依次对前面的元素进行相同的操作。n-1轮以后,排序成功。
假如有一轮 依次都没有交换的话,则说明有序了。
1. 相邻的预算两两比较,前者大于后者,彼此交换
2. 从第一对到最后一对,最大元素沉降到最后
3. 针对未排序部分,重复以上步骤,沉降次大值
4. 每次扫描越来越少的元素,直至不再发生交换
评价:
平均时间复杂度O(N的平方)
稳定排序
对数据的有序性非常敏感
冒泡排序效率总体来说是比较低的。平均时间复杂度O的平方,就是N的平方次。
但如果排序前就基本有序的话,则冒泡的效率就很高了。
1 #include
2 #include
3 //数组做参数,必须指定数组长度,数组不能返回
4 void bubble_sort(int data[],size_t size){//冒泡排序
5 int i,j;
6 for(i=0;i data[j+1]){//前面的比后面大
12 int temp = data[j];//交换算法
13 data[j] = data[j+1];
14 data[j+1] = temp;
15 flag=0;//发生了交换
16 }
17 }
18 if (flag) break;//没有交换就退出
19 }
20 }
21 int main()
22 {
23 int a[]={4,3,2,1,5,7,6};
24 bubble_sort(a,7);
25 int i;
26 for(i=0;i<7;i++)
27 printf("%d\n",a[i]);
28 return 0;
29 }
插入排序 1-n
算法:
第一个元素必然有序,然后把第二个到第n个元素插入前面的有序序列中,保证每次插入以后依然有序。
如何保持插入后依然有序呢?
把插入的数据和前面的数据进行比较,如果插入数据比前面数据小的话,前面的数据后移一位,插入数据
继续向前比较,如果插入数据不比前面的数据小的话,把数据插入到前面的数据后面即可。如果已经比较
到最前面的数据话,插入数据依然最小,插入数据放在第一位即可。
1 首元素自然有序
2 取出下一个元素,对已排序序列,从后向前扫描
3 大于被取出元素者后移
4 小于等于被取出元素者,将被取出元素插入其后
5 重复步骤2,直至处理完所有元素
评价
平均时间复杂度O(N的平方)
稳定排序
对数据的有序性非常敏感
不交换只移动,优于冒泡排序
1 #include
2 #include
3
4 void insert_sort(int data[],size_t size){//插入排序
5 int i,j;
6 for(i=1;i0 && inserted < data[j-1]);j--){//从最后向前依次比较
9 data[j] = data[j-1];//后移动一位,不需要交换
10 }
11 data[j] = inserted;//j就是inserted应该插入的下标
12
13 }
14
15 }
16
17 int main()
18 {
19 int a[]={4,3,2,1,5,7,6};
20 insert_sort(a,7);
21 int i;
22 for(i=0;i<7;i++)
23 printf("%d\n",a[i]);
24 return 0;
25 }
选择排序:
求数组最小值的算法:
int arr[];
int min = arr[0];
for(i=0;i
2 #include
3 void select_sort(int data[],size_t size){//选择排序
4 //思路:先从头开始记录最小值的下标,然后和i下标做数据交换
5 int i,j,min;
6 for (i=0;i
2 #include
3 void quick_sort(int data[],size_t left,size_t right){//快速排序,全或部分
4 size_t p = (left+right)/2;//以中间做基准
5 int num = data[p];//把基准数取出来。
6 int i = left;
7 int j = right;
8 while(i=p || numdata[j]);j--);
16 if (j>p)
17 {
18 data[p] = data[j];
19 p = j;
20 }
21 }
22 data[p] = num;//基准数放入应该在的位置,归位
23 if ((p - left) > 1) quick_sort(data,left,p-1);
24 if ((right - p) > 1) quick_sort(data,p+1,right);
25 }
26 int main()
27 {
28 int data[]={4,3,5,7,1,2,6};
29 quick_sort(data,0,6);
30 int i;
31 for (i=0;i<7;i++)
32 printf("%d\n",data[i]);
33 return 0;
34 }
查找算法 :
线性查找-----从头到尾找一遍,如果有,就退出循环,如果没有,循环结束就知道了。
二分查找-----必须是有序元素才能使用二分查找。
从中间点开始查找,如果大于 去后半部分,如果小于 去前半部分。
每次查找都是从中间点开始。
1 #include
2 #include
3 //返回find的下标,如果找不到返回-1;
4 int line_find(int data[],size_t size,int find){
5 int i;
6 for (i=0;idata[mid]) left = mid + 1;
21 else return mid;
22 }
23 return -1;
24 }
25 int main()
26 {
27 int data[]={7,2,4,5,1,3,6};
28 int in = line_find(data,7,2);
29 printf("in = %d\n",in);
30 int in1 = line_find(data,7,20);
31 printf("in1 = %d\n",in1);
32 int data2[]={1,2,3,4,5,6,7,8};
33 int in2 = half_find(data2,7,2);
34 printf("in2 = %d\n",in2);
35 int in3 = half_find(data2,7,20);
36 printf("in3 = %d\n",in3);
37
38 }
39