目录
一、排序算法
排序算法:将无序序列转换为有序序列的算法
- 内排:在计算机内存中实现的排序。(适用于数据量较小的情况)
- 外排:在内存和外部介质共同实现的排序(适用于数据量较大的情况)【先内存,后外部介质】
- 排序分类:
- 插入排序:直接插入排序、希尔排序
- 交换排序:冒泡排序、快速排序
- 选择排序:简单选择排序、堆排序
- 归并排序:二路归并(内、外),多路归并(外排)
- 基数排序
1.1 直接插入排序(插入排序)
插入排序:把待排序列分为有序区和无序区,拿无序区的每个元素,一次插入到有序区中,最终序列转换为有序序列的排序算法
时间复杂度:O(n^2) 稳定
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
程序要求:插入排序
*/
void paixu_inster(int arr[], int len);
void output(int arr[], int len);
void paixu_quick(int arr[], int low, int high);
int one_sort(int arr[], int low, int high);
int main()
{
int arr[] = {101, 34, 119, 1};
int len = sizeof(arr) / sizeof(arr[0]);
// paixu_quick(arr, 0, len - 1);
paixu_inster(arr, len);
output(arr, len);
system("pause");
return 0;
}
void paixu_inster(int arr[], int len)
{
int j;
for (int i = 1; i < len; i++) // 循环无序区,因为第一个默认有序区,故从1开始
{
int temp = *(arr + i); // 无序区往后逐渐移动,为无序区的第一个位置
for (j = i - 1; j >= 0 && temp < *(arr + j); j--)
{
*(arr + j + 1) = *(arr + j);
}
*(arr + j + 1) = temp;
output(arr, len);
}
}
void output(int arr[], int len)
{
for (int i = 0; i < len; i++)
{
printf("%d ", *(arr + i));
}
printf("\n");
}
void paixu_quick(int arr[], int low, int high)
{
if (low < high)
{
int mid = one_sort(arr, low, high); // 和基准值进行比较
paixu_quick(arr, low, mid - 1); // 递归左边
paixu_quick(arr, mid + 1, high); // 递归右边
}
}
int one_sort(int arr[], int low, int high)
{
int key = arr[low]; // 确定基准值
while (low < high)
{
while (low < high && key < arr[high])
{
high--;
}
arr[low] = arr[high];
while (low < high && key > arr[low])
{
low++;
}
arr[high] = arr[low];
}
arr[low] = key;
return low;
}
void paixu_selector(int arr[], int len)
{
for (int i = 0; i < len - 1; i++)
{
int min = i;
for (int j = i + 1; j < len; j++)
{
if (arr[j] < arr[min])
{
min = j;
}
}
if (min != i)
{
int temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
}
}
}
1.2 快速排序(交换排序)
快速排序:在待排序列中任意选择一个基准值,大于基准值放在后面,小于基准值的放在前边,一次比较,最终序列为转换为有序序列
时间复杂度:nlog2 n 稳定
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* function: 一次比较确定基准值
* @param [ in] 数组 最小下标low 最大下标high
* @param [out]
* @return 返回基准值的下标
*
*/
int one_sort(int arr[],int low,int high)
{
int key=arr[low];//确定基准值
while(low<high) //当low=high:表示找到了基准值的位置,反思想low<high就是循环的条件
{
while(low<high && key<=arr[high])//先比较数组的右半部分
{
high--;
}
arr[low]=arr[high];//基准值的右边存储大于基准值的值,如果小于基准值,则存到左边
while(low<high && key>=arr[low])//比较数组左半部分
{
low++;
}
arr[high]=arr[low];//基准值的左边存储小于基准值的值,如果大于基准值,则存到右边
}
arr[low]=key;//把基准值放在了应在的位置
return low;
}
/*
* function: 快速排序
* @param [ in]
* @param [out]
* @return 无返回
*/
void quick_sort(int arr[],int low,int high)
{
//1,当数组只有一个元素是,不需要排序 low==high
//2,当数组有多个元素,才需要排序low<high
if(low<high)
{
int mid=one_sort(arr,low,high);//和基准值比较
//递归左边
quick_sort(arr,low,mid-1);
//递归右边
quick_sort(arr,mid+1,high);
}
}
void output(int arr[],int len)
{
for(int i=0;i<len;i++)
{
printf("%d\t",arr[i]);
}
}
int main(int argc, const char *argv[])
{
int arr[]={23,1,45,56,36,3,90,6};
int len=sizeof(arr)/sizeof(arr[0]);
quick_sort(arr,0,len-1);
output(arr,len);
return 0;
}
1.3 冒泡排序(交换排序)
冒泡排序算法的原理如下:
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。
在这一点,最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较
时间复杂度:O(n^2) 稳定
for(int i=0;i<len-1;i++)//外层循环轮数
{
int count=0;
for(int j=0;j<n-i-1;j++)//内层循环次数
{
if(arr[j] >arr[j+1])
{
t=arr[j];arr[j]=arr[j+1];arr[j+1]=t;
count++;
}
}
if(count==0)
break;
}
1.4 简单选择排序(选择排序)
简单选择排序算法原理:每次从左至右扫描序列,记下最小值的位置。
默认第一个元素的下标为最值下标,一次向后比较最值,如果最值下标发生改变则交换,否则不交换
时间复杂度:O(n^2) 不稳定
for (int i = 0; i < len - 1; i++)
{
int min = i;
for (int j = i + 1; j < len; j++)
{
if (arr[j] < arr[min])
{
min = j;
}
}
if (min != i)
{
int temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
}
}
二、树
2.1 树的概念
- 树:是由根结点和若干棵子树构成的树形结构
- 是n(n>=0)个结点的有限集
- n>0时有且只有一个根结点
- 除根结点外,其余结点构成的互不相交的集合仍是一棵树
- 空树:不含任何结点的树n=0
- 根结点:只有一个结点n=1
- 普通树n>1: 多个结点
- 有序树:有序树【从上到下,从左到右】是树中每棵子树从左到右排列有一定顺序,不能互换的树
- 无序树:无序树是树中每棵子树从左到右排列无顺序,能互换的树
- 子树:是树中一个结点以及其下面所有的结点构成的树
2.2 树的类型
类型 | 概念 |
---|---|
结点 | 树中的数据元素 |
结点的度 | 结点含有子树的个数 |
根结点 | 没有双亲(前驱)结点的结点 |
分支结点(内部结点) | 度不为0的结点 |
叶结点(终端结点) | 度为0的结点 |
结点的层数 | 是从根结点到某结点所路径上的层数【根结点表示第一层】 |
树的深度(高度) | 是树中所有结点层数的最大值 |
树的度 | 各结点度的最大值 |
2.3 结点之间的关系
关系 | 概念 |
---|---|
父结点 | 是指当前结点的直接上级结点 |
孩子结点 | 是指当前结点的直接下级结点 |
兄弟结点 | 是由相同父结点的结点 |
祖先结点 | 是当前结点的直接及间接上级结点 |
子孙结点 | 是当前结点直接或间接下级结点 |
堂兄弟结点 | 是父结点在同一层的结点 |
2.4 森林
森林:是指0个或多个互不相交树的集合
2.5 二叉树
二叉树:树的度小于等于2
- 二叉树是每个结点最多有左、右两棵子树且严格区分顺序的树形结构(二叉树不可以互换)
- 二叉树的左边:左子树是以当前结点的左孩子结点作为根节点的子树
- 二叉树的右边:右子树是以当前结点的右孩子结点作为根节点的子树
2.6二叉树的特殊形态
- 空二叉树
- 只有一个根节点
- 只有左子树
- 只有右子树
- 既有左子树又有右子树
2.7 特殊二叉树
- 根节点:只有一个结点
- 空二叉树:没有根节点
- 斜树:斜树是所有的结点都只有左子树或者都只有右子树的二叉树
左斜树:左斜树是所有的结点都只有左子树的斜树
右斜树:右斜树是所有的结点都只有右子树的斜树
- 满二叉树:满二叉树是最后一层是叶子结点,其余结点度是2的二叉树
- 叶子结点只能出现在最下面一层
- 非叶子结点的度数一定是2
- 同样深度的二叉树中,满二叉树的结点的个数最多,叶子结点最多
- 完全二叉树:完全二叉树是在一棵满二叉树基础上自左向右连续增加叶子结点得到的二叉树
- 只有最后两层有叶子结点
- 除最后一层是一棵满二叉树
- 最后一层的叶子结点集中在左边连续的位置
2.8 二叉树的性质
- 在非空二叉树的第i层上,至多有2^(i-1)个结点(i>=1) 【1----->2^(i-1)】
- 在深度为K的二叉树上总结点最多有(2^k)-1个结点(k>=1) 【2^(k-1) ------》 2^(k)-1】
- 在任意一棵二叉树中,叶子结点的数目比度数为2的结点的数目多1
- 叶子结点n0:度为0的结点
- 度为2的结点n2
- 度为1的结点n1
- n0=n2+1
总结:
- 点数:n=n0+n1+n2
- 点数等于树杈数加1:n=n1+2*n2+1
5. 完全二叉树结点的编号方法是从上到下,从左到右根节点为1号结点设完全二叉树的结点数为n,某结点编号为i
- 若 i=1,则该结点是二叉树的根,无双亲,否则,其双亲结点是编号为 i/2的结点
- 若 2*i>n,则该结点无左孩子,否则,其左孩子结点是编号为 2i 的结点
- 若 2*i+1>n,则该结点无右孩子结点,否则,其右孩子结点是编号为2i+1 的结点
2.9 二叉树的存储结构
- 顺序存储
- 链式存储(二叉链表)
2.10 二叉树的遍历
----以根的位置划分—
- 先序遍历:满足根、左、右的遍历方法 (根左右)
- 中序遍历:满足左、根、右的遍历方法 (左根右)
- 后序遍历:满足左、右、根的遍历方法 (左右根)