直接选择排序:
原理:每次从未排序的序列中找到最小值,记录并最后存放到已排序序列的结尾
性能:时间复杂度为O(N^2)空间复杂度为O(1),排序是不稳定的(把最小值交换到已排序的结尾导致的),每次都能确定一个元素所在的最终位置,比较次数与初始序列无关。
void select_sort(int value[], int length)
{
int i,j;
for(i=0;i<length-1;i++)
{
int min=i;
for(j=i+1;j<length;j++)
{
if(value[min]>value[j])
{
min=j;
}
}
if(min!=i)
{
swap(value[i],value[min]);
}
}
}
快速排序
快速排序是对冒泡排序的一种改进。由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列
对于基准数的选择,本程序使用中间值作为基准数,下面是程序实现:
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
void swap(int *a ,int *b)
{
int t = *a;
*a = *b;
*b = t;
}
int partition(int array[],int l,int r)
{
int pivot = array[r];
int curpos = l;
int j ;
for( j = l;j<r;j++)
{
if(array[j] < pivot)
{
swap(&array[j],&array[curpos]);
curpos++;
}
}
swap(&array[r],&array[curpos]);
return curpos;
}
void quicksort(int array[],int l,int r)
{
int interval;
if(l < r)
{
interval = partition(array,l,r);
quicksort(array,l,interval-1);
quicksort(array,interval+1,r);
}
}
int test_quicksort()
{ int i;
int number = 12;
int *array = malloc(number*sizeof(int));
if(array == NULL)
{
printf("malloc failed\n");
return -1;
}
printf("----------------------------------------before quick sort--------------\n");
srand(time(NULL));
for(i = 0;i<number;i++)
{
array[i] = rand()%1000;
printf("\tarray[%d] = %d\n",i,array[i]);
}
printf("----------------------------------------after quick sort-----------------\n");
quicksort(array,0,number-1);
for(i = 0;i<number;i++)
{
printf("\tarray[%d] = %d\n",i,array[i]);
}
return 0;
}
int main()
{
test_quicksort();
}
归并排序:
分而治之思想:
1.Divide: 将n个元素平均划分为各含n/2个元素的子序列;
2.Conquer: 递归的解决两个规模为n/2的子问题;
3.Combine: 合并两个已排序的子序列。
性能:时间复杂度总是为O(NlogN),空间复杂度为O(N),算法与初始序列无关,排序也是稳定的
优化:
1. 在规模较小时,合并排序可以采用直接插入
2.在写法上,可以在生成辅助数组时,两头小,中间大,这是不需要在后边加两个while循环进行判断,只需一次比完。
比较v[indexA]=7和v[indexB]=12,将较小的v[indexA]取出来放到临时向量tempArray中,然后indexA加1
代码:
#include<stdlib.h>
typedef int RecType;//要排序元素类型
void Merge(RecType *R,int low,int m,int high)
{
//将两个有序的子文件R[low..m)和R[m+1..high]归并成一个有序的子文件R[low..high]
int i=low,j=m+1,p=0; //置初始值
RecType *R1; //R1是局部向量
R1=(RecType *)malloc((high-low+1)*sizeof(RecType));
if(!R1)
{
return; //申请空间失败
}
while(i<=m&&j<=high) //两子文件非空时取其小者输出到R1[p]上
{
R1[p++]=(R[i]<=R[j])?R[i++]:R[j++];
}
while(i<=m) //若第1个子文件非空,则复制剩余记录到R1中
{
R1[p++]=R[i++];
}
while(j<=high) //若第2个子文件非空,则复制剩余记录到R1中
{
R1[p++]=R[j++];
}
for(p=0,i=low;i<=high;p++,i++)
{
R[i]=R1[p]; //归并完成后将结果复制回R[low..high]
}
}
void MergeSort(RecType R[],int low,int high)
{
//用分治法对R[low..high]进行二路归并排序
int mid;
if(low<high)
{ //区间长度大于1
mid=(low+high)/2; //分解
MergeSort(R,low,mid); //递归地对R[low..mid]排序
MergeSort(R,mid+1,high); //递归地对R[mid+1..high]排序
Merge(R,low,mid,high); //组合,将两个有序区归并为一个有序区
}
}
void main()
{
int a[7]={49,38,65,97,76,13,27}; //这里对8个元素进行排序
int low=0,high=6; //初始化low和high的值
printf("Before merge sort: ");
for(int i=low;i<=high;i++)
{
printf("%d ",a[i]); //输出测试
}
printf("/n");
MergeSort(a,low,high);
printf("After merge sort: ");
for( i=low;i<=high;i++)
{
printf("%d ",a[i]); //输出测试
}
printf("/n");
}
优化代码:
void Merge(int arr[],int temp_arr[],int left,int mid,int right)
{
//简单归并,先复制到temp_arr,再进行归并
int i;
for(i=left,i<=right;i++)
{
temp_arr[i]=arr[i];
}
int pa = left,
int pb=mid+1;
int index = left;
while(pa<=mid && pb<=right)
{
//两子文件非空时取其小者输出到arr上
if(temp_arr[pa] <= temp_arr[pb])
{
arr[index++] = temp_arr[pa++];
}
else
{
arr[index++] = temp_arr[pb++];
}
}
//复制没有比较完子表中的元素
while(pa<=mid) //若第1个子文件非空,则复制剩余记录到R1中
{
arr[index++]=temp_arr[pa++];
}
while(pb<=right) //若第2个子文件非空,则复制剩余记录到R1中
{
arr[index++]=temp_arr[pb++];
}
}
void Merge_improve(int arr[],int temp_arr[],int left,int mid,int right)
{
//优化归并;复制时,两头小,中间大,一次比较完
int i;
for(i=left;i<=mid;i++)
{
temp_arr[i]=arr[i];
}
for(i=mid+1;i<=right;i++)
{
temp_arr[i]=arr[right+mid+1-i];
}
int pa = left;
int pb = right;
int p = left;
while(p<=right)
{
if(temp_arr[pa]<=temp_arr[pb])
{
arr[p++]=temp_arr[pa++];
}
else
{
arr[p++]=temp_arr[pb--];
}
}
}
void MergeSort(int arr[],int temp_arr[],int left,int right)
{
//用分治法对R[left..right]进行二路归并排序
if(left<right)
{ //区间长度大于1
int mid=(left+right)/2; //分解
MergeSort(arr,temp_arr,0,mid); //递归地对R[low..mid]排序
MergeSort(arr,temp_arr,mid+1,right); //递归地对R[mid+1..high]排序
Merge(arr,temp_arr,left,mid,right); //组合,将两个有序区归并为一个有序区
}
}
void MergeSort(int arr[],int len)
{
int *temp_arr = (int *)malloc(sizeof(int)*len);
Mergesort(arr,temp_arr,0,len-1);
}
原理
堆的性质:
是一棵完全二叉树
每个节点的值都大于或等于其子节点的值,为最大堆;反之为最小堆。
堆排序思想:
将待排序的序列构造成一个最大堆,此时序列的最大值为根节点
依次将根节点与待排序序列的最后一个元素交换
再维护从根节点到该元素的前一个节点为最大堆,如此往复,最终得到一个递增序列
性能
时间复杂度为O(NlogN),空间复杂度为O(1),因为利用的排序空间仍然是初始的序列,并未开辟新空间。算法是不稳定的,与初始序列无关。
使用场景
想知道最大值或最小值时,比如优先级队列,作业调度等场景。
代码
void shiftDown(int arr[], int start, int end)
{
//从start出发到end,调整为最大堆
int dad = start;
int son = dad * 2 + 1;
while (son <= end){
//先选取子节点中较大的
if (son + 1 <= end && arr[son] < arr[son + 1])
{
son++;
}
//若子节点比父节点大,则交换,继续往子节点寻找;否则退出
if (arr[dad] < arr[son])
{
swap(arr[dad], arr[son]);
dad = son;
son = dad * 2 + 1;
}
else
{ break; }
}
}
void heap_sort(int arr[], int len)
{ //先调整为最大堆,再依次与第一个交换,进行调整,最后构成最小堆
for (int i = (len - 2) / 2; i >= 0; i--)
{
//len为总长度,最后一个为len-1,所以父节点为 (len-1-1)/2
shiftDown(arr,i,len-1);
}
for (int i = len - 1; i >= 0; i--)
{
swap(arr[i], arr[0]);
shiftDown(arr, 0,i-1);
}
}
#include <stdio.h>
#define N 1000
#define INF 999999999
int h[N];
//调整堆(迭代法)
//n:规模 i:二叉子堆的堆顶
void
heapAdjust(int n, int par)
{
int tmp, pos, lc, rc;
while (par <= n/2) {
tmp = h[par]; //记录父母结点键值
lc = par<<1;
rc = lc+1;
pos = par;
//父母结点至多更新2次
if (h[par] < h[lc]) {
h[par] = h[lc];
pos = lc;
}
if (rc <= n && h[par] < h[rc]) {
h[par] = h[rc];
pos = rc;
}
if (pos == par) //无更新即无需调整
break;
else
h[pos] = tmp;
par = pos; //假设这个位置的结点是“父母结点”
}
}
//创建堆
//规模为n的堆,对其父母结点,自底向上自右向左地调整堆
void
createHeap(int n)
{
int i;
for (i = n/2; i != 0; i--) {
heapAdjust(n, i);
}
}
void
heapSort(int n)
{
int ntimes = n;
while (ntimes--) {
printf("%d\n", h[1]);
h[1] = h[n];
h[n--] = 0; //堆清零
heapAdjust(n, 1);
}
}
int
main(void)
{
int n, i;
scanf("%d", &n);
h[0] = INF;
for (i = 1; i != n+1; i++) {
scanf("%d", &h[i]);
}
createHeap(n);
heapSort(n);
return 0;
}