概述
插入排序
基本思想:每一步将一个待排序的元素,按其排序码的大小,插入到前面已经排好的序的一组元素的合适的位置上去,直到元素全部插完为止。
1.直接插入排序
基本思想:
当插入第i个元素时,前面的array[0]、array[1]、array[2]……array[i-1]已经排好序,此时用array[i]的排序码与array[i-1]、array[i-2]……的排序码进行比较,找到插入位置将array[i]插入,原来位置上的元素顺序右移。
//插入排序-直接排序
void InsertSort(int *a, int n)
{
assert(a != NULL);
int i = 0;
for (; i < n - 1; ++i)
{
int end = i;
int tmp = a[end + 1];
while (end >= 0 && a[end]>tmp)
{
a[end + 1] = a[end];
--end;
}
a[end + 1] = tmp;
void PrintArray(int * a, int n)}
}
2. 二分插入排序
优点:查找更快,减少比较操作的次数,效率更高。
//二分插入排序
void InsertSort_OP(int *a, int n)
{
for (int i = 0; i < n-1; i++)
{
//在已序序列中查找待插入元素的位置
int left = 0;
int right = i;
int key = a[i];
while (left <= right)
{
int mid = left + ((right - left) >> 1);
if (key < a[mid])
right = mid - 1;
else
left = mid + 1;
}
//搬移元素
int end = i - 1;
while (end >= left)
{
a[end + 1] = a[end];
end--;
}
//插入元素
a[left] = key;
}
}
3.希尔排序
基本思想:
希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因D.L.Shell于1959年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
int ShellSort(int *a, int n)
{
assert(a != NULL);
int gap = n;
int i = 0;
if (gap > 1)
{
gap = gap / 3 + 1;
}
for (; i < gap; ++i)
{
int end = i;
int tmp = a[end + gap];
while (end >= 0 && a[end]>tmp)
{
a[end + gap] = a[end];
end -= gap;
}
a[end + gap] = tmp;
}
}
选择排序
基本思想:
每一趟(第i趟,i,...,n-2)在后面n-i个待排序的数据元素个数集合中选出关键码最小的数据元素,作为有序元素序列的第i个元素。待到第n-2趟做完,待排序元素集合中只剩下1个元素,排序结束。
1.直接选择排序
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法(比如序列[5, 5, 3]第一次就将第一个[5]与[3]交换,导致第一个5挪动到第二个5后面)。
void Swap(int *x1, int *x2)
{
int tmp = *x1;
*x1 = *x2;
*x2 = tmp;
}
//选择排序
void SelectSort(int *a, int n)
{
assert(a != NULL);
int begin = 0, end = n - 1;
while (begin < end)
{
int minindex = begin, maxindex = begin;
int i = 0;
for (i = begin; i <= end; ++i)
{
if (a[i]>a[maxindex])
maxindex = i;
if (a[i] < a[minindex])
minindex = i;
}
Swap(&a[begin], &a[minindex]);
if (begin == maxindex)
maxindex = minindex;
Swap(&a[end], &a[maxindex]);
++begin;
--end;
}
}
2.堆排序
- 把堆顶array[0]元素和当前堆的最后一个元素交换
- 堆元素个数减1
- 由于第一步后根节点不再满足最堆定义,向下调整根结点
把一颗完全二叉树调整为堆,以及每次将堆顶元素交换后进行调整的时间复杂度均为O(log2n),所以堆排序的时间复杂度为:O(n*log2n)
void AdjustDown(int *a, int n, int root)
{
int parent = root;
int child = root * 2 + 1;
while (child < n)
{
if (child + 1 < n&&a[child + 1] > a[child])
{
++child;
}
if (a[child]>a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
}
}
//堆排序
void HeapSort(int *a, int n)
{
assert(a != NULL);
for (int i = (n - 2) / 2; i >= 0; --i)
{
AdjustDown(a, n, i);
}
int end = n - 1;
while (end > 0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
--end;
}
}
交换排序
1.冒泡排序
基本思想:
设序列中有n个数据元素,循环进行n-1趟如下排序:
第1趟:依次比较相邻两个数据元素(i = 0,1,2,…, n-2),若array[i]>array[i+1],则交换两个元
素,否则不交换,这样数值最大的数据元素将被防止在a[n-1]中;
第2趟:数据个数减1,即数据元素个数为n-1,操作方式和1类似,排完之后数据序列中的次大元素
保存在array[n-2]中;
当n-1趟结束时,排序结束。
//冒泡排序
void BubbleSort(int *a, int n)
{
assert(a != NULL);
int end = n;
while (end > 0)
{
for (int i = 1; i > end; ++i)
{
if (a[i - 1]>a[i])
{
Swap(a[i - 1], a[i]);
//flag = true;
}
}
--end;
}
}
2.快速排序
基本思想:
通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
递归
int QuickSort_OP(int *a, int begin, int end)//递归
{
if (begin < end)
{
int div = PartSort03/02/01(a, begin, end);
QuickSort(a, begin, div-1);
QuickSort(a, div+1, end);
}
}
非递归
int QuickSort(int *a, int left, int right)
{
Stack st;
StackInit();
StackPush(&st, left);
StackPush(&st, right);
while (!StackEmpty())
{
int end = StackTop(&st);
StackPop(&st);
int begin = StackTop(&st);
StackPop(&st);
int div = PartSort01(a, begin, end);
if (begin < div - 1)
{
StackPush(&st, begin);
StackPush(&st, div - 1);
}
if (end > div + 1)
{
StackPush(&st, div + 1);
StackPush(&st, end);
}
}
}
第一种:左右指针法
每次选一个基准值,这里简单起见,直接每次取要排序区域的最后一位数据作为基准值。
设置两个指针,begin指针 指向要排序区域的起始位置,end指针 则指向要排序区域的最后位置。
begin指针寻找比基准值大的元素,end指针寻找比基准值小的元素。
交换begin 指针与end 指针的内容。
退出循环后,交换基准值与结束时begin,end指针所指向的值。
int PartSort01(int *a, int n,int begin, int end)
{
//int begin = 0, end = n - 1;
int key = a[end];
int keyindex = end;
while (begin < end)
{
while (begin < end&&a[begin] <= key)//找大
begin++;
while (begin < end&&a[begin] >= key)//找小
end--;
Swap(&a[begin], &a[end]);
}
Swap(&a[begin], &a[keyindex]);
return begin;
}
第二种: 挖坑法
每次选一个基准值,这里简单起见,直接每次取要排序区域的最后一位数据作为基准值。
设置两个指针,begin指针 指向要排序区域的起始位置,end指针 则指向要排序区域的最后位置。
begin指针寻找比基准值大的元素,将begin指针所指的值放入key的位置上。
end指针寻找比基准值小的元素,将end指针所指向的值放入空出的位置上。
退出循环后,最后将key值填入空出的位置上。
int PartSort02(int *a, int begin, int end)
{
assert(a != NULL);
int key = a[end];
while (begin < end)
{
while (begin < end&&a[begin] <= key)//找大
{
begin++;
a[end] = a[begin];
}
while (begin < end&&a[begin] >= key)//找小
{
end--;
a[begin] = a[end];
}
}
a[begin] = key;
return begin;
}
第三种:前后指针法
定义两个指针,一前一后,cur(前)指向起始位置,prev(后)指向cur的前一个位置;选择数组最后一个元素作为key(right)
实现过程:cur找比key小的数,prev在cur没有找到的情况下,一直不动(即保证prev一直指向比key大的数);直到cur找到比key小的数(+ +prev && prev != cur时)然后++cur,prev仍然不动。
直到cur走到right前一个位置,终止循环。最后++prev,交换prev和right的值。返回中间位置prev。最后再继续递归。
int PartSort03(int * a, int begin, int end)
{
int prev = begin - 1;
int cur = begin;
int key = a[end];
while (cur < end)
{
if (a[cur] < key&&++prev != cur)//找小
Swap(&a[cur], &a[prev]);
++cur;
}
Swap(&a[++prev], &a[end]);
return prev;
}
第四种 三数取中法
在待排序序列的第一个元素,中间元素和最后一个元素中取大小位于中间的那个元素。
//三数取中法
int GetmidIndex(int *a, int begin, int end)
{
int mid = begin + ((end - begin) >> 1);
if (a[begin] < a[mid])
{
if (a[mid] < a[end])
return mid;
else if (a[begin] < a[end])
return a[end];
else
return a[begin];
}
else
{
if (a[end] < a[mid])
return mid;
else if (a[begin < a[end]])
return begin;
else
return end;
}
}
归并排序
基本思想:
基本思想:将待排序的元素序列分成两个长度相等的子序列,为每一个子序列排序,然后将他们合并成一个序列。合并两个子序列的过程称为两路归并。
//归并排序
void MergeSort(int *a, int n)
{
assert(a != NULL);
int *tmp = (int *)malloc(sizeof(int)*n);
_MergeSort(a, 0, n - 1, tmp);
free(tmp);
}
void _MergeSort(int *a, int begin, int end, int *tmp)
{
int mid = begin + ((end - begin) >> 1);
_MergeSort(a, begin, mid , tmp);
_MergeSort(a, mid + 1, end, tmp);
Merge(a, begin, mid, mid + 1, end, tmp);
}
void Merge(int *a, int begin1, int begin2, int end1, int end2, int *tmp)
{
int index = begin1;
int n = end2 - begin1;
int start = begin1;
while (begin1 < end1&&begin2 < end2) //从头开始比较大小,小的放在新空间中,继续往后走
{
if (a[begin1] < a[begin2])
tmp[index++] = a[begin1++];
else
tmp[index++] = a[begin2++];
}
while (begin1 < end1)//将a[begin1]-a[end1]的元素放入新空间中
tmp[index++] = a[begin1++];
while (begin2 < end2)
tmp[index++]=a[begin2++];
memcpy(a + start, start, sizeof(int)*(n + 1));
}
计数排序
利用哈希的方法,将每个数据出现的次数都统计下来。哈希表是顺序的,所以我们统计完后直接遍历哈希表,将数据再重写回原数据空间就可以完成排序。
//计数排序
void CountSort(int *a, int n)
{
assert(a != NULL);
int max = a[0], min = a[0];
int i = 0;
for (; i < n; ++i)
{
if (a[i]>max)
max = a[i];
if (a[i] < min)
min = a[i];
}
int range = max - min + 1;
int *counts = (int*)malloc(range*sizeof(int));
memset(counts, 0, sizeof(int)*range);
for (i = 0; i < n;++i)
{
//1 2 1 4 1 1 9
counts[a[i] - min]++; //counts[]++为统计原数组中数字出现的次数
}
int index = 0;
for (i = 0; i < range; ++i)
{
while (counts[i]--)
{
a[index++] = i + min;
}
}
}
排序算法的稳定性
如果在元素序列中有两个元素R[i]和R[j],他们的排序码K[i]==K[j],且在排序之前,R[i]在R[j]前面。若在排序后,R[i]仍然在R[j]前面,则称这个算法是稳定的。否则,这个算法是不稳定的。
最后
以上就是生动世界为你收集整理的各种排序算法的实现的全部内容,希望文章能够帮你解决各种排序算法的实现所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复