一、插入排序
有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法——插入排序法,
(1)算法适用于少量数据的排序,
(2)时间复杂度为O(n^2)。
(3)是稳定的排序方法。
插入排序的基本思想是:每步将一个待排序的记录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。
————分为直接插入排序法和折半插入(二分插入)
1、直接插入排序
直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。
void InsertSort(int* array, int size)//插入排序
{
int i = 1;
for (; i < size; ++i)
{
int key = array[i];
int end = i - 1;
//查找待插入元素的位置
while (end >= 0 && key < array[end])//升序
{
array[end + 1] = array[end];
--end;
}
array[end + 1] = key;
}
}
2、折半插入排序(二分插入排序)
void InsertSort_OP(int* array, int size)//插入排序—利用二分法优化
{
int i = 1;
for (; i < size; ++i)
{
int key = array[i];
int begin = 0;
int end = i - 1;
int mid = -1;
//通过二分查找找待插入元素的位置
while (begin <= end)
{
mid = begin + ((end - begin) >> 1);
if (key >= array[mid])
begin = mid + 1;
else
end = mid - 1;
}
//搬移元素
end = i - 1;
while (end >= begin)
{
array[end + 1] = array[end];
--end;
}
array[begin] = key;
}
}
二、希尔排序
希尔排序是插入排序的一种又称“缩小增量排序”,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
void ShellSort(int* array, int size)//希尔排序
{
int i = 1;
int gap = size;//间隔
while (gap > 1)
{
gap = gap / 3 + 1;
for (; i < size; ++i)
{
int key = array[i];
int end = i - gap;
//查找待插入元素的位置
while (end >= 0 && key < array[end])
{
array[end + gap] = array[end];
end -= gap;
}
array[end + gap] = key;
}
}
}
三、选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
选择排序是不稳定的排序方法(比如序列[5,5,3]第一次就将第一个[5]与[3]交换,导致第一个5挪动到第二个5后面)。
1、方法一
void SelectSort(int* array, int size)//选择排序
{
int i = 0;
int j = 0;
int maxPos = 0;
for(; i < size - 1; ++i)
{
for (j = 1; j < size - i; ++j)
{
if (array[j]>array[maxPos])
maxPos = j;
}
if (maxPos != size - 1)
Swap(&array[maxPos], &array[j]);
}
}
2、方法二
void SelectSort_OP(int* array, int size)//选择排序——优化
{
int begin = 0;
int end = size - 1;
while (begin < end)
{
int index = begin + 1;
int maxPos = begin;
int minPos = begin;
while (index++ < end)
{
if (array[index] > array[maxPos])
maxPos = index;
if (array[index] < array[minPos])
minPos = index;
}
if (maxPos != end)
Swap(&array[maxPos], &array[end]);
if (minPos == end)
minPos = maxPos;
if (minPos != begin)
Swap(&array[minPos], &array[begin]);
begin++;
end--;
}
}
void Swap(int* pLeft, int* pRight)//交换
{
int tmp = 0;
assert(pLeft);
assert(pRight);
tmp = *pLeft;
*pLeft = *pRight;
*pRight = tmp;
}
四、堆排序
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。
void HeapSort(int* array, int size)//堆排序
{
//创建堆
int end = size - 1;
int root = ((size - 2) >> 1);
for (; root >= 0; --root)
HeapAdjust(array, root, size);
//排序——删除
while (end)
{
Swap(&array[0], &array[end]);
HeapAdjust(array, 0, end);
--end;
}
}
void HeapAdjust(int* array, int parent, int size)//调整——下
{
int child = parent * 2 + 1;
while (child < size)
{
if (child + 1 < size && array[child + 1] > array[child])
child += 1;
if (array[parent] < array[child])
{
Swap(&array[parent], &array[child]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
五、冒泡排序
它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
冒泡排序算法的原理如下:
(1)比较相邻的元素。如果第一个比第二个大,就交换他们两个。
(2)对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
(3)针对所有的元素重复以上的步骤,除了最后一个。
(4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
void BubbleSort(int* array, int size)//冒泡排序
{
int i = 0;
int j = 0;
for (i = 0; i < size - 1; i++)
{
for (j = 0; j < size - 1 - i; j++)
{
if (array[j] > array[j + 1])
{
DataType tmp = array[j];
array[j] = array[j + 1];
array[j + 1] = tmp;
}
}
}
}
六、快速排序
快速排序(Quicksort)是对冒泡排序的一种改进。
它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
快速排序(快排):以下为递归和非递归
方法一: ——递归
void QuickSort(int* array, int left, int right)//快排
{
if (right - left < 16)//有多个元素
InsertSort(array, right - left);
else
{
int div = Partion2(array, left, right);
QuickSort(array, left, div);
QuickSort(array, div + 1, right);
}
}
//此处分隔方法有很多种,暂时只给一种
int Partion2(int* array, int left, int right)//分隔—挖坑法
{
int key = array[right-1];
int begin = left;
int end = right - 1;
while (begin < end)
{
//从前往后找比基准值key大的元素
while (begin < end && array[begin] <= key)
++begin;
if (begin < end)
{
array[end] = array[begin];
end--;
}
//从后往前找比基准值key小的元素
while (begin < end && array[end] >= key)
--end;
if (begin < end)
{
array[begin] = array[end];
begin++;
}
}
array[begin] = key;
return begin;
}
方法二: ——非递归(需要使用到栈的操作)
void QuickSortNor(int* array, int size)//快排——非递归
{
Stack s;
StackInit(&s);
StackPush(&s, size);
StackPush(&s, 0);
while (!StackEmpty(&s))
{
int left = 0;
int right = 0;
left = StackTop(&s);
StackPop(&s);
if (left < right)
{
int div = Partion1(array, left, right);//基准值的位置
StackPush(&s, right);//右部分右边界压栈
StackPush(&s, div + 1);//右部分左边界压栈
StackPush(&s, div);//左部分右边界压栈
StackPush(&s, left);//左部分左边界压栈
}
}
}
七、计数排序
计数排序是一个非基于比较的排序算法,
它的优势在于在对一定范围内的整数排序时,
它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。
当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(n*log(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(n*log(n)), 如归并排序,堆排序)
计数排序对输入的数据有附加的限制条件:
(1)输入的线性表的元素属于有限偏序集S;
(2)设输入的线性表的长度为n,|S|=k(表示集合S中元素的总数目为k),则k=O(n)。
在这两个条件下,计数排序的复杂性为O(n)。
void CountSort(int* array, int size)//计数排序——鸽巢原理
{
int maxValue = array[0];
int minValue = array[0];
int i = 0;
int range = 0;//辅助空间
int* Count = NULL;
int index = 0;
for (; i < size; ++i)
{
if (array[i]>maxValue)
maxValue = array[i];
if (array[i] < minValue)
minValue = array[i];
}
range = maxValue - minValue + 1;
Count = (int*)malloc(range*sizeof(int));
if (NULL == Count)
{
assert(0);
return;
}
memset(Count, 0, range*sizeof(int));
for (i = 0; i < size; ++i)
Count[array[i] - minValue]++;
for (i = 0; i < range; ++i)
{
while (Count[i]--)
array[index++] = i + minValue;
}
free(Count);
}
八、归并排序
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并操作(merge),也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。
如设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;
第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
总的比较次数为:3+4+4=11,;
逆序数为14;
归并操作的工作原理如下:
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
方法一:递归法
void MergeSort(int* array, int size)//归并排序
{
int* tmp = (int*)malloc(size*sizeof(array[0]));
if (NULL == tmp)
{
assert(0);
return;
}
_MergeSort(array, 0, size,tmp);//tmp—临时空间
free(tmp);
}
void MergeData(int* array, int left, int mid, int right, int* tmp)//搬元素、归并
{
int beginL = left,endL = mid;
int beginR = mid, endR = right;
int index = left;
while (beginL < endL && beginR < endR)
{
if (array[beginL] <= array[beginR])
tmp[index++] = array[beginL++];
else
tmp[index++] = array[beginR++];
}
while (beginL < endL)
tmp[index++] = array[beginL++];
while (beginR < endR)
tmp[index++] = array[beginR++];
}
void _MergeSort(int* array, int left, int right, int* tmp)//排序
{
if (right - left > 1)
{
int mid = left + ((right - left) >> 1);
_MergeSort(array, left, mid, tmp);//排左边
_MergeSort(array, mid, right, tmp);//排右边
MergeData(array, left, mid, right, tmp);//把两个分组归并成一个
memcpy(array + left, tmp + left, sizeof(array[0])*(right - left));
}
}
方法二:非递归
void MergeSortNor(int* array, int size)//归并排序——非递归
{
int i = 0;
int gap = 1;
int* tmp = (int*)malloc(size*sizeof(array[0]));
if (NULL == tmp)
{
assert(0);
return;
}
while (gap < size)
{
for ( i = 0; i < size; ++i)
{
int left = i;
int mid = left + gap;
int right = mid + gap;
if (mid>size)
mid = size;
if (right>size)
right = size;
MergeData(array, left, mid, right, tmp);
}
memcpy(array, tmp, size*sizeof(array[0]));
gap *= 2;
}
free(tmp);
}
九、基数排序
(1)基数排序(radix sort)属于“分配式排序”(distribution sort)又称“桶子法”
(2)它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,
(3)基数排序法是属于稳定性的排序,
(4)其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,
(5)在某些时候,基数排序法的效率高于其它的稳定性排序法。
void RadixSort(int* array, int size)//基数排序(桶排序)——LSD
{
int* bucket = (int*)malloc(size*sizeof(array[0]));//辅助空间
if (NULL == bucket)
{
assert(0);
return;
}
_RadixSort(array, size, bucket);
free(bucket);
}
void _RadixSort(int* array, int size, int* bucket)
{
int i = 0;
int bitIndex = 0;//要统计的是哪个位 个、十、百...为0即个位
int bitCount = GetBitCount(array, size);//计算是几位数
int radix = 1;//基数
for (; bitIndex < bitCount; ++bitIndex)
{
//统计每个桶中元素的个数
int Count[10] = { 0 };//桶中存放元素的个数的数组
int startAddr[10] = { 0 };
for (i = 0; i < size; ++i)
Count[array[i] / radix % 10]++;
//计算每个桶的起始地址
for (i = 1; i < 10; ++i)
startAddr[i] = startAddr[i - 1] + Count[i - 1];
//放置元素到辅助空间
for (i = 0; i < size; ++i)
{
int bucketNo = array[i] / radix % 10;
bucket[startAddr[bucketNo]++] = array[i];
}
//回收
memcpy(array, bucket, size*sizeof(array[0]));
}
radix *= 10;
}
int GetBitCount(int* array, int size)//最大的那一个有多少位
{
int count = 1;
int i = 0;
int radix = 10;//基数
for (; i < size; ++i)
{
while (array[i] >= radix)//如果大于等于radix;位数+1
{
++count;
radix *= 10;
}
}
return count;
}