- 直接插入排序(类似于打牌)
1.1:解决的关键问题:
1.1.1:构造初始有序序列
1.1.2:查找待插入记录的插入位置
void InsertSort(int a[], int n) {
//i从2开始,i默认1位有序,就此分为有序区和无序区
for (int i = 2; i <= n; i++)
{
a[0] = a[i];//设置哨兵好处:1:保存a[i]的值,避免移动过程中被覆盖
//2:充当哨兵,做循环的条件
int j;
for ( j = i-1; a[j] > a[0]; j--)
{
//出现的错误:弄成了j++,而应该是j+1,不然就成了死循环
a[j+1] = a[j];//把已经比较过的值进行右移
//if (a[i] > a[j])
//{
// break;//找a[i]插入的位置
}
a[j+1] = a[0];
}
}
1.2:二分插入排序
int BinarySearchIndex(int a[], int left, int right, int x)
{
while (left <= right)
{
int mid = (left + right) / 2;
if (a[mid] > x)
{
right = mid - 1;
}//就算是等于往后面插入更加优化
else
{
left = mid + 1;
}
}
return left;
}
void InsertSortBinarySearch(int a[], int n) {
//i从2开始,i默认1位有序,就此分为有序区和无序区
for (int i = 2; i <= n; i++)
{
int temp = a[i];
//没有借鉴直接插入排序的经验,保存好变量值,移动过程中会覆盖值
int index = BinarySearchIndex(a, 1, i - 1, a[i]);
for (int j = i - 1; j >= index; j--)
{
//不要习惯性的用j++,否则不能跳出循环
a[j+1] = a[j];
}
a[index] = temp;
}
}
1.3:希尔排序:
1.3.1:改进的点:
1.3.1.1:基本有序的情况下,插入排序效率高
1.3.1.2:数量少的情况下效率高
void ShellSort(int a[], int n) {
//外层是个衡量指标,每次跨度距离
for (int d = n / 2; d >= 1; d /= 2) {//以增量d为直接插入排序
for (int i = d+1; i <= n; i += d)//前面的d项默认为有序
{
a[0] = a[i];//只是暂存a[i]的值,不起到哨兵的作用,因为循环推出的条件是j<0||a[0]>=a[j]
int j;
for (j = i - d; j > 0 && a[j] > a[0]; j -= d)
{
a[j + d] = a[j];
}
a[j + d] = a[0];
}
}
}
2:交换排序
2.1:冒泡排序
void BubbleSort(int a[], int n)
{
for (int i = 0; i < n - 1; i++)//只需要进行n-1趟
{
int flag = 0;
for (int j = 1; j < n - i; j++)
{
if (a[j] < a[j - 1])
{
swap(a[j], a[j - 1]);
flag = 1;
}
}
if (flag == 0)break;
}
}
2.2:快速排序:对冒泡排序的改进,冒只能一次移动一个,而快排是从两端向中间进行,移动的距离比较远。
2.2.1:解决的关键问题:
2.2.1.1:如何进行一次划分
//5:快速排序
int Partition(int r[], int first, int end)
{
int i = first, j = end;
while (i < j)
{
//其实这里应该不用临时变量储存,因为轴值就存储在a[i]或者a[j]中
while (i<j&&a[i] <= a[j]) j--;
if (i < j) {
swap(a[i], a[j]);
i++;
}
while (i < j&&a[i]<=a[j]) i++;
if (i < j){
swap(a[i], a[j]);
j--;
}
}
return i;
}
//5.1:快速排序的递归写法
void QuickSort(int a[], int first, int end)
{
if (first < end) {//如果区间长度大于1,则继续划分,否则结束
int pivot = Partition(a, first, end);
QuickSort(a, first, pivot - 1);//递归地往两边划分
QuickSort(a, pivot + 1,end );
}
}
3:选择排序
//2:选择排序
void SelectSort(int a[], int n) {
for (int i = 0; i < n - 1; i++)//只需要n-1趟
{
int k=i;
//从剩余的元素中找出最小的元素
for (int j = i; j < n; j++)
{
if (a[i] > a[j])k = j;
}
if (k != i)swap(a[i], a[k]);
}
}
3.1;堆排序:改进(减少关键码的比较次数)
3.1.1:筛选:
void sift(int r[], int k, int m)
{//要筛选结点的编号为k,堆中最后一个结点的编号为m
int i, j;
i = k; j = 2 * i;//根据树的节点的特点
while (j <= m) //筛选还没有进行到叶子,即存在孩子节点
{
if (j < m && r[j] < r[j + 1]) j++; //左右孩子中取较大者
if (r[i] > r[j]) break;
else { swap(a[i], a[j]); i = j; j = 2 * i; }
}
}
3.1.2:
建堆的过程:此序列是按完全二叉树的顺序存储,所以建堆的过程就是反复筛选的过程;
3.1.3:处理堆顶
void HeapSort(int r[], int n)
{
int i;
for (i = n / 2; i >= 1; i--) //初建堆
sift(r, i, n);
for (i = 1; i > n; i++)
{
swap(r[1],r[n - i + 1]); //移走堆顶
sift(r, 1, n - i); //重建堆
}
}
4:归并排序
4.1:二路归并排序解决的关键问题:
4.1.1:如何构造初始有序序列?看成长度为1的有序序列
4.1.2:一次归并:将相邻的有序序列归并成一个有序序列?
void Merge(int r[], int r1[], int s, int m, int t)
{
int i, j, k;
i = s; j = m + 1; k = s;
while (i <= m && j <= t)
{
if (r[i] <= r[j]) r1[k++] = r[i++];
else r1[k++] = r[j++];
}
if (i <= m) while (i <= m) //收尾处理
r1[k++] = r[i++]; //前一个子序列
else while (j <= t)
r1[k++] = r[j++]; //后一个子序列
}
4.1.3:怎么完成一趟归并?主要是看i与n以及h长度之间关系
void MergePass(int r[], int r1[], int n, int h)
{
int i,k;
i = 1;
while (i<=n-2*h + 1) //情况1:还可以继续进行归并
{
Merge(r, r1, i, i + h - 1, i + 2 * h - 1);
i += 2 * h;
}
if (i<=n - h + 1) Merge(r, r1, i, i + h - 1, n); //情况2:剩最后两个序列,完成后退出
else for (k = i; k <= n; k++) //情况3:最后一个有序表,直接进行复制
r1[k] = r[k];
}
4.1.4:怎么控制归并的结束?
//步骤3:控制二路归并的结束
void MergeSort(int r[], int r1[], int n)
{
int h;
h = 1;
while (h < n)
{
MergePass(r, r1, n, h);
h = 2 * h;
//弄偶数次的话保证倒回到数组r中
MergePass(r1, r, n, h);
h = 2 * h;
}
}
4.2:递归实现(理解栈的实现过程):
//递归实现
void MergeSort2(int r[], int r1[], int s, int t)
{
int m;
if (s == t) r1[s] = r[s]; //递归出口:只有一个元素时
else {
m = (s + t) / 2;
MergeSort2(r, r1, s, m); //归并排序前半个子序列
MergeSort2(r, r1, m + 1, t); //归并排序后半个子序列
Merge(r1, r, s, m, t); // 将两个已排序的子序列归并
}
}
5:桶排序和基数排序的思想弄懂了,但是代码没调通。
6:比较: