(一)插入排序
插入排序基本思想:从初始的子集合开始,不断地将新的元素插入到已排序好的子集合当中的合适位置。(未排序的插入到已排序当中)具体分为直接插入排序和希尔排序两种。
①直接插入排序
void InsertSort(int a[], int n)//直接插入排序
{
int i, j, temp;
for (i = 0; i < n-1; i++)//循环n-1次
{
temp = a[i + 1];//将未排序的元素暂存在temp里面
j = i;
while (j > -1 && temp < a[j])
{
a[j + 1] = a[j];//将已排序子集合里面最右边的数赋值给未排序的第一个元素
j--; //若能在已排序好的元素里面一直找到比temp大的数,j就前移,直到找到比temp小的数或者j值为-1,j停止
}
a[j + 1] = temp;//当上述循环结束之后,此时j+1的位置就是插入的合适位置
}
}
直接插入排序的动画演示:
②希尔排序
希尔排序基本思想:(事实上是经过分组整理之后,变得更加有序才使用直接插入排序)
将待排序的元素分成若干个小组,对同一个小组使用直接插入排序。
void ShellSort(int a[], int n, int d[], int m)//希尔排序 d[]代表增量 n代表次数
{ //d[3]={6,3,1} ,m=3
int i, j, k, s, span,temp;
for (s = 0; s < m; s++) //一共m次循环
{
span = d[s]; //取本次循环的增量
for (k = 0; k < span; k++) //一共span个小组
{ //组内是直接插入排序,但是并不是相邻比较,而是隔span比较
for (i = 0; i < n - span; i++) //以下为直接插入排序的操作方法
{
temp = a[i + span];
j = i;
while (j > -1 && temp < a[j])
{
a[j + span] = a[j];
j = j - span;
}
a[j + span] = temp;
}
}
}
}
希尔排序的动画演示:
(二)选择排序
选择排序基本思想:每次从待排序集合里面选取最小或最大的元素放在元素集合的最前面或最后面。一般有直接选择排序和堆排序两种算法。
①直接选择排序
void SelectSort(int a[], int n) //直接选择排序
{
int i, j, temp,small;
for (i = 0; i < n - 1; i++) //第一次做n-1次循环
{
small = i; //设a[0]为最小值
for (j = i + 1; j < n; j++)
{
if (a[j] < a[small]) //将a[small]与后面n-1个数比较,找到最小值
small = j; //标记最小值位置
}
if (small != i) //上述循环做完一次之后,若不是同一个位置,就交换元素
{
temp = a[i];
a[i] = a[small];
a[small] = temp;
}
}
}
直接选择排序动画演示:
②堆排序算法
堆排序基本思想:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
堆的定义:堆分为最大堆(根结点比左右孩子结点都要大)和最小堆(根结点比左右孩子结点都要小)两种。
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子
第一步:写出满足最大堆的函数
void CreatHeap(int a[], int n, int h) //满足最大堆的函数
{
int i, j, flag,temp;
i = h; //建立堆的二叉树根结点下标
j = 2 * h + 1; //i的左孩子结点下标
temp = a[i]; //保存根结点
flag = 0;
while (j < n && flag != 1)
{
if (j < n - 1 && a[j] < a[j + 1]) j++;//找到左右孩子的最大值,j为下标
if (temp > a[j]) flag = 1; //标记结束筛选条件
else //若根结点不是最大,就将a[j](左右孩子最大值)上移
{
a[i] = a[j]; //将最大值赋给根结点
i = j; //将最大孩子结点赋给i,方便后面a[i]=temp,调换
j = 2 * i + 1;//判断j是否超过元素个数
}
}
a[i] = temp; //把最初的a[i]赋给最后的a[j],对换
}第二步:建立最大堆
void InitCreatHeap(int a[], int n) //创建最大堆
{
for (int i = (n - 2) / 2; i >= 0; i--) //第一个非叶子结点开始
CreatHeap(a, n, i);
}
a.将无序序列构建成一个堆,根据升序降序需求选择大顶堆(升序)或小顶堆(降序);
b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
第三步:堆排序算法
void HeapSort(int a[], int n) //堆排序算法
{
int i,temp;
InitCreatHeap(a, n);//初始化最大堆
for (i = n - 1; i > 0; i--)//做n-1次
{
temp = a[0]; //将第一个元素与最后一个元素对换
a[0] = a[i];
a[i] = temp;
CreatHeap(a, i, 0); //调整根结点满足堆的定义
}
}
堆排序算法动画演示:
(三)交换排序
交换排序基本思想:利用交换元素的位置进行排序的方法。常用的交换排序方法有冒泡排序和快速排序法。其中快速排序法是一种分区交换的排序方法。
①冒泡排序
冒泡排序基本思想:每一趟均比较相邻的两个元素。若前一个比后一个大就交换它们的位置,否则就不交换。相当于每次找到了最大元素,将其放在最后一位。
void BubbleSort(int a[], int n) //冒泡排序
{
int i, j, temp,flag=1; //flag优化算法
for (i = 1; i < n&&flag==1; i++)
{flag=0;
for (j = 0; j < n - i; j++)//做n-1次 n-2次 直到0次结束
{
if (a[j] > a[j+1]) //比较相邻元素大小
{flag=1;
temp = a[j]; //交换
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
}
冒泡排序动画演示:
②快速排序
快速排序基本思想:选一个数作为基数(这里我选的是第一个数),大于这个基数的放到右边,小于这个基数的放到左边,等于这个基数的数可以放到左边或右边,看自己习惯,这里我是放到了左边,一趟结束后,将基数放到中间分隔的位置,第二趟将数组从基数的位置分成两半,分割后的两个的数组继续重复以上步骤,选基数,将小数放在基数左边,将大数放到基数的右边,在分割数组,直到数组不能再分为止,排序结束。
例如从小到大排序:
1. 第一趟,第一个数为基数temp,设置两个指针left = 0,right = n.length,
①从right开始与基数temp比较,如果n[right]>基数temp,则right指针向前移一位,继续与基数temp比较,直到不满足n[right]>基数temp
②将n[right]赋给n[left]
③从left开始与基数temp比较,如果n[left]<=基数temp,则left指针向后移一位,继续与基数temp比较,直到不满足n[left]<=基数temp
④将n[left]赋给n[right]
⑤重复①-④步,直到left==right结束,将基数temp赋给n[left]
2. 第二趟,将数组从中间分隔,每个数组再进行第1步的操作,然后再将分隔后的数组进行分隔再快排,
3. 递归重复分隔快排,直到数组不能再分,也就是只剩下一个元素的时候,结束递归,排序完成
根据思路分析,第一趟的执行流程如下图所示:
void QuickSort(int a[], int low, int high) //快速排序算法
{
int i = low, j = high, temp = a[low];//先把a[i]取出来暂存
while (i < j)
{
while(i < j && a[j] >= temp) j--; //最→边找到比temp小的数就停止
if (i < j)
{
a[i] = a[j]; //将这个数赋给a[i],第一次为a[0]
i++; //移到左边扫描
}
while (i < j && a[i] < temp) i++; //最左边找到比temp大的数就停止
if (i < j)
{
a[j] = a[i]; //将这个数赋给刚刚j停止的地方
j--; //移到右边扫描
}
}
a[i] = temp; //扫描完一遍之后,当i=j时循环停止。将temp赋给ij的位置
if (low < i)
QuickSort(a, low, i - 1); //对左边子集合扫描
if (high > i)
QuickSort(a, j + 1, high); //对右边子集合扫描
}
快速排序动画演示: