直接插入排序:
算法:1,将序列中的第1个记录看成是有序的子序列
2。从第二个记录起按关键字大小逐个进行插入,直至整个序列变成按关键字有效序列为止
(1)稳定性
直接插入排序是稳定的排序方法。
(2)算法效率
a.时间复杂度
最好情况:比较O(n),移动O(1);
最坏情况:比较O(n2),移动O(n2);
平均O(n2)
b.空间复杂度
O(1)。
#include<stdio.h>
//直接插入排序
void InsertionSort(int l[],int len)
{
int i, j;
for (i = 1; i <len; ++i)
{
if (*(l+i)< *(l+i-1))
{
*(l+len) = *(l+i);//复制为监视哨,做了点小改动,将监视哨放在最后面
for (j = i - 1; *(l+len) < *(l+j); --j)
{
*(l+j+1) = *(l+j);//记录后移
}
*(l+j + 1) = *(l+len);//插入到正确位置
}
}
}
int main()
{
int a[7] = {19,34,24,89,80,10};
InsertionSort(a,6);
for (int i = 0; i < 6; ++i)
printf("%d\n", a[i]);
return 0;
}
折半插入排序思想:
1.在直接插入排序进行第i个元素时,l[0]到l[i-1]是一个按关键字的有序序列
2.可以利用折半法查找实现在‘l[0]到l[i-1]’中插入l[i]的位置
(1)稳定性
折半插入排序是不稳定的排序方法。
(2)算法效率
a.时间复杂度
最好情况:比较O(n),移动O(1);
最坏情况:比较O(log2n!),移动O(n2);
平均O(n2)
b.空间复杂度
O(1)。
#include<stdio.h>
//折半插入排序
void BiInsertionSort(int l[],int len)
{
int i, j,low,high,m;
for (i = 1; i <len; ++i)
{
if (*(l+i)< *(l+i-1))
{
*(l+len) = *(l+i);//复制为监视哨,做了点小改动,将监视哨放在最后面
low = 0; high = i - 1;
while (low<=high)
{
m = (low + high)/2;//折半
if (*(l + len) < *(l + m))
high = m - 1;
else low = m + 1;
}
for (j = i - 1;j>high; --j)
{
*(l+j+1) = *(l+j);//记录后移
}
*(l+high+ 1) = *(l+len);//插入到正确位置
}
}
}
int main()
{
int a[7] = {19,34,24,89,80,10};
BiInsertionSort(a,6);
for (int i = 0; i < 6; ++i)
printf("%d\n", a[i]);
return 0;
}
希尔排序:将记录序列分成若干子序列,分别对每个子序列进行插入排序,其中d为增量,它的值在排序的过程中从大到小逐渐缩小,直至最后一趟的排序减为1
希尔排序从d+1个位置开始,希尔排序完成增量为d的排序后,序列的特征是:子序列l[i],l[i+d],l[i+2d]是有序的。
当希尔序列的增量为1的排序过程是直接插入排序
(1)稳定性
希尔排序是不稳定的排序方法。
(2)算法效率
(1)时间复杂度
平均O(n1.3)到平均O(n1.5)
(2)空间复杂度
O(1)。
#include<stdio.h>
//希尔排序算法
void ShellInsert(int l[],int len,int d)
{
int i, j,low,high,m;
for (i = d; i < len; ++i)
{
if (l[i] < l[i - d])
{
l[len] = l[i];//监视哨,暂存
for (j = i - d; j>=0&&(l[j]>l[len]); j=j-d)//注意下标从0开始,所以要改为j>=0
{
l[j + d] = l[j];//记录后移,寻找插入点
}
l[j+ d] = l[len];//插入
}
}
}
//增量为d[]的希尔排序
void ShellSort(int l[],int lenl, int lend, int d[])
{
for (int k = 0; k < lend; ++k)
{
//一趟增量为d[k]的插入排序
ShellInsert(l, lenl, d[k]);
}
}
int main()
{
int a[7] = {19,34,24,89,80,10};
int d[3] = {5,3,1 };
ShellSort(a,6,3,d);
for (int i = 0; i < 6; ++i)
printf("%d\n", a[i]);
return 0;
}
冒泡排序:
1.从第一个记录开始,两两进行比较,如果l[i]>l[i+1],则两个记录进行交换
2.第1趟比较结果将序列中关键字最大的记录放置到最后一个位置,成为沉底,则最小的则上浮一个位置
(1)稳定性
起泡排序是稳定的排序方法。
(2)时间是复杂性
最好情况:比较O(n), 移动O(1)
最坏情况:比较O(n2), 移动O(n2)
平均情况:O(n2)
(3)空间复杂性
O(1)
#include<stdio.h>
//冒泡排序算法
void BubbleSort(int l[], int len)
{
int i, j,temp;
for (i = 0; i <len-1; i++)
{
for (j = 0; j <len - i-1; j++)
{
if (l[j] > l[j+1])
{
temp = l[j];
l[j] = l[j+1];
l[j+1] =temp;
}
}
}
}
int main()
{
int a[6] = {19,34,24,89,80,10 };
BubbleSort(a, 6);
for (int i = 0; i < 6; ++i)
printf("%d\n", a[i]);
return 0;
}
快速排序:
任取待排序对象序列中的某个对象v,按照对象的关键字大小,将整个序列划分为左右两个子序列:1左侧子序列的关键字都小于等于v的关键字2。右侧子序列中的所有对象的关键字都大于等于v的关键字 3.对象v排在这两个子序列中间。
快速排序一趟后序列有什么特征?
存在一个元素,这个元素左边元素不大于它的关键字,右边元素不小于它的关键字
最好情况的时间复杂度为O(nlog2n)
平均时间复杂度也是O(nlog2n)
由于快速排序是一个递归过程,需一个栈的附加空间,栈的平均深度为O(log2n)。
#include<stdio.h>
//快速排序算法
void Qsort(int l[],int low,int high)
{
if (low >= high)
return;
int temp,ktemp;
int i = low;
int j = high;
temp = l[low];
while (low < high)
{
while (low < high && temp<=l[high])
high--;
l[low] = l[high];
while (low < high && temp>=l[low])
low++;
l[high] = l[low];
}
l[low] = temp;//枢轴记录到位
Qsort(l, i, low - 1);
Qsort(l, low + 1, j);
}
int main()
{
int a[6] = {19,34,24,89,80,10};
Qsort(a,0,5);
for (int i = 0; i < 6; ++i)
printf("%d\n", a[i]);
return 0;
}
简单选择排序:1.从n个关键字中选择一个最小值,确定第一个2.第二次再从剩余元素中选择一个最小值,确定第二个
简单选择排序一趟后序列有什么特征?
最小元素排在第一位。
算法概要:1.第一趟排序是在无序区l[0]到l[n-1]中选出关键字最小的记录,然后与l[0]进行交换,确定最小值
2.第二趟排序是在l[1]到l[n-1]之间选关键字最小的记录,将它与a[1]交换,确定次小值
3.第i趟排序是在l[i-1]与l[n-1]中选择关键字最小的记录,将它与l[i-1]进行交换
4.共n-1趟排序
4. 算法分析
(1)稳定性
简单选择排序方法是不稳定的。
(2)时间复杂度
比较O(n2),移动最好O(1),最差O(n)
(3)空间复杂度
为O(1)。
#include<stdio.h>
//简单选择排序算法
void Selectsort(int l[], int len)
{
int low,temp;
for (int i = 0; i < len; i++)
{
low = i;
for (int j = i; j < len; j++)
{
if (l[j] < l[low])
low = j;
}
if (i != low)
{
temp = l[low];
l[low] = l[i];
l[i] = temp;
}
}
}
int main()
{
int a[6] = { 19,34,24,89,80,10 };
Selectsort(a,6);
for (int i = 0; i < 6; ++i)
printf("%d\n", a[i]);
return 0;
}
堆排序属于选择排序:出发点是利用选择排序己经发生的比较,记住比较结果,减少重复比较的次数
大根堆中根节点关键字最大
利用大根堆进行排序,根节点与最后一个节点交换
利用大根堆排序一趟后序列有什么特点?
最大元素在最后。
堆排序算法概要:
1.按关键字建立大根堆
2.输出堆顶元素,采用堆顶元素l[0]与最后一个元素l[n-1]交换,最大元素得到正确的排序位置
3.此时前n-1个元素不满足堆的特性,需重建堆
4.循环执行2,3直到排序完成
筛选算法实例:1.先按照无序序列建立完全二叉树 2.从最后一个非终端结点开始建堆
#include<stdio.h>
void swap(int* a, int* b)
{
int temp = *b;
*b = *a;
*a = temp;
}
//堆排序算法
void HeapAdjust(int l[], int start, int end)
{
int dad = start;//建立父节点指标和子节点指标
int son = dad * 2+1;
while (son <= end)
{
if (son + 1 <= end && l[son] < l[son + 1]) //先比较两个子节点,选择最大的
son++;
if (l[dad] > l[son])
return;
else
{
swap(&l[dad], &l[son]);
dad = son;
son = dad * 2 + 1;
}
}
}
void Heapsort(int l[], int len)
{
int i;
for (i = len / 2 - 1; i >= 0; i--)//建立大根堆
HeapAdjust(l, i, len - 1);
//先将第一个元素与已排好元素前一位做交换,再重新做调整,直到排序完毕
for (i = len - 1; i > 0; i--)
{
swap(&l[0], &l[i] );
HeapAdjust(l, 0, i - 1);
}
}
int main()
{
int a[6] = { 19,34,24,89,80,10 };
Heapsort(a,6);
for (int i = 0; i < 6; ++i)
printf("%d\n", a[i]);
return 0;
}
归并排序
#include<stdio.h>
void swap(int* a, int* b)
{
int temp = *b;
*b = *a;
*a = temp;
}
void MergeSort(int la[], int lb[],int k, int m,int n)
{
int i,j;//m是lena-1,n是lenc-1,j是标记b的下标,k是标记a的下标
for (i = k ,j = m + 1;i <= m &&j <= n;k++)
{
if (la[i] < la[j])lb[k] = la[i++];
else
lb[k] = la[j++];
}
if (i <= m)
while (k <= n&& i <= m)
lb[k++] = la[i++];
if (j <= n)
while (k <= n&&j<= n)
lb[k++] = la[j++];
}
int main()
{
int a[5];
for (int i = 0; i < 5; i++)
a[i] = 2 * i +1;
int b[6];
for (int i = 0; i < 6; i++)
b[i] = 2 * (i+1);
int c[11];
int lc[11];
int i;
for (i = 0; i <5; i++)
lc[i] = a[i];
for (i = 0; i <6; i++)
lc[i + 5] = b[i];
MergeSort(lc,c,0,4,10);
for (int i = 0; i < 11; ++i)
printf("%d\n", c[i]);
return 0;
}
归并排序一趟后序列有什么特征?
两两有序(二路归并)
归并排序总共需要多少趟?
log2^n
2-路归并排序的思想:
1.将两个记录看成是n个长度为1的有序子表
2.将两两相邻的有序子表进行归并,若子表数为奇数,则留下的一个子表直接进入下一次归并
3.重复2,直到归并成只剩一个长度为n的有序表
归并排序核心算法—有序序列长度为l时的归并
1.l[0]到l[l-1],l[l]到l[2l-1]是有序的
2.l[i]到l[l-1+i],l[l+i]到l[i+2l-1]是有序的
3.两两归并,Merge(la[],lb[],i,l-1+i,i+2*l-1)
#include<stdio.h>
void swap(int* a, int* b)
{
int temp = *b;
*b = *a;
*a = temp;
}
void Merge(int la[], int lb[], int k, int m, int n)
{
int i, j;
for (i = k, j = m + 1; i <= m && j <= n; k++)
{
if (la[i] < la[j])lb[k] = la[i++];
else
lb[k] = la[j++];
}
if(i<=m)
while (i<=m&&k<=n)
{
lb[k++] = la[i++];
}
if (j<= n)
while (j <= n && k <= n)
{
lb[k++] = la[j++];
}
}
void Mergesort(int la[], int lb[], int n, int l)
{
int i;
for ( i = 0; 2*l+i-1 <n-1; i = i+l * 2)
{
Merge(la, lb, i, l + i - 1, l * 2 + i - 1);
}
Merge(la, lb, i, l + i - 1, n-1);
}
void Msort(int la[], int len)
{
int l = 1;
int lb[11];
for (int i = 0; i < 11; ++i)
{
lb[i] = la[i];
}
while (l < len)
{
Mergesort(la, lb, len, l);
l = l * 2;
Mergesort(lb, la, len, l);
l = l * 2;
}
}
int main()
{
int a[5];
for (int i = 0; i < 5; i++)
a[i] = 2 * i +1;
int b[6];
for (int i = 0; i < 6; i++)
b[i] = 2 * (i+1);
int c[11];
int lc[11];
int i;
for (i = 0; i <5; i++)
lc[i] = a[i];
for (i = 0; i <6; i++)
lc[i + 5] = b[i];
Msort(lc,11);
for (int i = 0; i < 11; ++i)
printf("%d\n", lc[i]);
return 0;
}
基数排序的两个主要步骤(操作)是什么?分配和收集
顺序基数排序:
设待排序列A的关键字最大是figure位的整数
1.从最低位,个位开始,扫描关键字的pass位i,等于0的插入Q[0],等于1的插入Q[1]…
2.将Q[0],Q[1],…Q[9]中的数据依次收集到A[]中
3.pass+1直到figure,重复执行1,2两步
#include<stdio.h>
#include<stdlib.h>
void sort(int A[], int n)
{
int i = 0, j = n, h = 0; int temp;
while (h < 2)
{
while (i < j)//三个元素的快速排序
{
while ((i < j) && A[i] == h)
i++;
while ((i < j) && A[j] != h)
j--;
if (i < j) { temp = A[i]; A[i] = A[j]; A[j] = temp; }
}
j = n; h = h + 1;
}
}
int main()
{
int a[3] = { 0,2,1 };
sort(a, 2);
for (int i = 0; i < 3; ++i)
{
printf("%d\n", a[i]);
}
return 0;
}