注:部分代码来自网上
冒泡排序:
相临数据不停交换,这样每一趟后,后面一个数据就是最大或者最小的了。
可以采取保存最后一次交换的位置来减少交换次数,因为这代表此位置后面数据没有发生交换,已经有序列了。
此算法缺点是交换的次数过多
//冒泡排序3
void BubbleSort3(int a[], int n)
{
int j, k;
int flag;
flag = n;
while (flag > 0)
{
k = flag;
flag = 0;
for (j = 1; j < k; j++)//K后面已经有序
if (a[j - 1] > a[j])//相临的交换
{
Swap(a[j - 1], a[j]);
flag = j;//发生交换的最后点
}
}
}
//冒泡排序3
void BubbleSort3(int a[], int n)
{
int j, k;
int flag;
flag = n;
while (flag > 0)
{
k = flag;
flag = 0;
for (j = 1; j < k; j++)//K后面已经有序
if (a[j - 1] > a[j])//相临的交换
{
Swap(a[j - 1], a[j]);
flag = j;//发生交换的最后点
}
}
}
选择排序:
每趟找一个最大或者最小的,交换到开关,比较次数同冒泡算法差不多,但交换每遍只进行一次,算法复杂度都是O(n*n),但执行操作少一点,速度理论上快些
void Selectsort(int a[], int n)
{
int i, j, nMinIndex;
for (i = 0; i < n; i++)
{
nMinIndex = i; //找最小元素的位置
for (j = i + 1; j < n; j++)
if (a[j] < a[nMinIndex])
nMinIndex = j;
Swap(a[i], a[nMinIndex]); //将这个元素放到无序区的开头
}
}
{
int i, j, nMinIndex;
for (i = 0; i < n; i++)
{
nMinIndex = i; //找最小元素的位置
for (j = i + 1; j < n; j++)
if (a[j] < a[nMinIndex])
nMinIndex = j;
Swap(a[i], a[nMinIndex]); //将这个元素放到无序区的开头
}
}
快速排序:
分治思想,每次找到一个“基准数”,把大于此数的放在右边,小于此数的放在左边。然后分成了左右两边,再把左右两边分别进行前面的操作,直到无法再分为止,
具体实施的时候,过程类似数列左右两边开始找数,左边找大于基数的,右边找小于基数的,然后互相交换位置。依此类推。
如果遇到倒序或者全部相等就惨了,需要进行优化。
//快速排序
void quick_sort(int s[], int l, int r)
{
if (l < r)
{
//if(r -l < 16){//当数据量较小时候,再进行分组快排,分组次数较多,不划算,可以采用短数量排序的优势排序方法:插入排序
// 进行插入排序//-----------------------------也是优化办法之一
}
//Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换
//基数X 可以取left mid right 或者三者中大小处于中间的数,或者随机一个位置等
int i = l, j = r, x = s[l];
while (i < j)
{
while(i < j && s[j] >= x) // 从右向左找第一个小于x的数, 此时真要判断等于?这样如果遇到等于也进行跳过,如果全部等于时候,划分的话就应该会划分成1:[n -1],那样每次只划分出一个元素。
//如果不加等于判断,那相等的元素会每次划分进入下一次的递归排序过程,左右各一半。其实跟基数X相等的元素可以不排序了
//那样就有了另外一种优化,每次递归分三段,中间一段是全部相等的X,不再进行排序
//但感觉有一定优化性,特别相等的很多时候,但同时需要交换保存相等的元素到中间,也有消耗,优化的应该有限。
j--;
if(i < j)
s[i++] = s[j];//A
while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
i++;
if(i < j)
s[j--] = s[i];//与A处一起,完成一次交换
}
s[i] = x;//左右已经分两边了,空出了中间位置,给基数用。
quick_sort(s, l, i - 1); // 递归调用
quick_sort(s, i + 1, r);
}
//快速排序
void quick_sort(int s[], int l, int r)
{
if (l < r)
{
//if(r -l < 16){//当数据量较小时候,再进行分组快排,分组次数较多,不划算,可以采用短数量排序的优势排序方法:插入排序
// 进行插入排序//-----------------------------也是优化办法之一
}
//Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换
//基数X 可以取left mid right 或者三者中大小处于中间的数,或者随机一个位置等
int i = l, j = r, x = s[l];
while (i < j)
{
while(i < j && s[j] >= x) // 从右向左找第一个小于x的数, 此时真要判断等于?这样如果遇到等于也进行跳过,如果全部等于时候,划分的话就应该会划分成1:[n -1],那样每次只划分出一个元素。
//如果不加等于判断,那相等的元素会每次划分进入下一次的递归排序过程,左右各一半。其实跟基数X相等的元素可以不排序了
//那样就有了另外一种优化,每次递归分三段,中间一段是全部相等的X,不再进行排序
//但感觉有一定优化性,特别相等的很多时候,但同时需要交换保存相等的元素到中间,也有消耗,优化的应该有限。
j--;
if(i < j)
s[i++] = s[j];//A
while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
i++;
if(i < j)
s[j--] = s[i];//与A处一起,完成一次交换
}
s[i] = x;//左右已经分两边了,空出了中间位置,给基数用。
quick_sort(s, l, i - 1); // 递归调用
quick_sort(s, i + 1, r);
}
}
插入排序
从左边开始,认为左边有序,然后把右边数一个一个插入到左边去。
具体实施过程可能是左边的有序数列,从有序数列右边开始,一个一个的向右移动一位,直到移出到新要插入的那个数值的位置为止,然后插入它。
//插入排序
void Insertsort2(int a[], int n)
{
int i, j;
for (i = 1; i < n; i++)
if (a[i] < a[i - 1])//i是右边要插入到左边的数,如果已经是大的,也就是代表有序了。跳过。
{
int temp = a[i];//
for (j = i - 1; j >= 0 && a[j] > temp; j--)//开始与要插入的数进行比较移位,直到移出合适位置
a[j + 1] = a[j];
//插入排序
void Insertsort2(int a[], int n)
{
int i, j;
for (i = 1; i < n; i++)
if (a[i] < a[i - 1])//i是右边要插入到左边的数,如果已经是大的,也就是代表有序了。跳过。
{
int temp = a[i];//
for (j = i - 1; j >= 0 && a[j] > temp; j--)//开始与要插入的数进行比较移位,直到移出合适位置
a[j + 1] = a[j];
//OK
a[j + 1] = temp;
}
}
a[j + 1] = temp;
}
}
希尔排序
插入排序的一种优化,插入排序每趟最大的消耗在于要移动出新插入数据的空位,随着有序的数据长的时候,移动的次数可能很多。如果进行分组,每一组数据量并不大,这样少量数据的插入排序,消耗就会小很多。
希尔排序就是基于分组思想,但分组并非连续的分组,而是把相隔固定值的全部数据分为一组。而这个间隔值会慢慢的变小,直到间隔只有一个,也就是完全分为一组了,进行最后的一次插入排序。
其优化在于小组内快排较快,后面分组时候间隔较小,分组的元素会变多,但由于之前分组进行过大概的排序,已经基本有序,所以后面的插入排序会较快。
void shellsort2(int a[], int n)
{
int j, gap;
for (gap = n / 2; gap > 0; gap /= 2)//固定间隔值步步移到1
for (j = gap; j < n; j++)//从数组第gap个元素开始
if (a[j] < a[j - gap])//每个元素与自己组内的数据进行直接插入排序
{
int temp = a[j];
int k = j - gap;
while (k >= 0 && a[k] > temp)
{
a[k + gap] = a[k];
k -= gap;
}
//找到位置,插入之
a[k + gap] = temp;
}
}
归并排序:
也是分治办法 由于要归并两个有序列的 需要分配中间缓存数组
//将有二个有序数列a[first...mid]和a[mid...last]合并。
void mergearray(int a[], int first, int mid, int last, int temp[])
{
int i = first, j = mid + 1;
int m = mid, n = last;
int k = 0;
while (i <= m && j <= n)
{
if (a[i] <= a[j])
temp[k++] = a[i++];
else
temp[k++] = a[j++];
}
while (i <= m)
temp[k++] = a[i++];
while (j <= n)
temp[k++] = a[j++];
for (i = 0; i < k; i++)//归并完后,数据回归到a[]
a[first + i] = temp[i];
}
void mergesort(int a[], int first, int last, int temp[])
{
if (first < last)
{
int mid = (first + last) / 2;
mergesort(a, first, mid, temp); //左边有序
mergesort(a, mid + 1, last, temp); //右边有序
mergearray(a, first, mid, last, temp); //再将二个有序数列合并
}
}
bool MergeSort(int a[], int n)
{
int *p = new int[n];
if (p == NULL)
return false;
mergesort(a, 0, n - 1, p);
delete[] p;
return true;
}
//将有二个有序数列a[first...mid]和a[mid...last]合并。
void mergearray(int a[], int first, int mid, int last, int temp[])
{
int i = first, j = mid + 1;
int m = mid, n = last;
int k = 0;
while (i <= m && j <= n)
{
if (a[i] <= a[j])
temp[k++] = a[i++];
else
temp[k++] = a[j++];
}
while (i <= m)
temp[k++] = a[i++];
while (j <= n)
temp[k++] = a[j++];
for (i = 0; i < k; i++)//归并完后,数据回归到a[]
a[first + i] = temp[i];
}
void mergesort(int a[], int first, int last, int temp[])
{
if (first < last)
{
int mid = (first + last) / 2;
mergesort(a, first, mid, temp); //左边有序
mergesort(a, mid + 1, last, temp); //右边有序
mergearray(a, first, mid, last, temp); //再将二个有序数列合并
}
}
bool MergeSort(int a[], int n)
{
int *p = new int[n];
if (p == NULL)
return false;
mergesort(a, 0, n - 1, p);
delete[] p;
return true;
}
堆排序:
堆的定义具体网上有,大概是个完全二叉树,然后根节点都大于或者小于叶子节点。
一,数据保存采用线性保存办法,所以取左右子节点时候需要index*2 + 1跳着移动。
二,建立堆,需要直观的表达是从金字塔的倒数第二排开始,一层层的向上走,比较移动后,保存每个节点的左右子节点有大于或者小于它的性质。
三,建立后,堆本身最上面的一个塔尖数是最大或者最小的数了,把它移到最后节点n-1.封存T除掉。同时把a[n-1]放截尖。
四,此时堆又不对了,需要进行调整,需要再次把塔尖的数进行下沉操作。
下沉操作:把塔尖的数据下向移动,直到发现父母节点比子节点都小,代表OK了。
有个特性是,由于下沉过程中,只移动了数据有才进行递归移动,其它分支没有移动的,并没有改变它们的性质。
void HeapSort( int num[] , int size) |
21 | { |
22 | int i; |
23 | int iLength=size; |
24 | |
25 | PrintHeap( "Befor Sort:" ,num,iLength); |
26 | |
27 | BuildHeap(num,size); // 建立小顶堆 |
28 | |
29 | for (i = iLength - 1; i >= 1; i--) { |
30 | Swap(num, 0, i); // 交换 |
31 | size--; // 每交换一次让规模减少一次 |
32 | PercolateDown(num, 0,size); // 将新的首元素下滤操作 |
33 | PrintHeap( "Sort Heap:" ,num,iLength); |
34 | } |
35 | } |
36 |
37 | // 建堆方法,只需线性时间建好 |
38 | void BuildHeap( int num[] , int size) { |
39 | int i; |
40 | for (i = size / 2 - 1; i >= 0; i--) { // 对前一半的节点(解释为“从最后一个非叶子节点开始,将每个父节点都调整为最小堆”更合理一些) |
41 | PercolateDown(num, i,size); // 进行下滤操作 |
42 | PrintHeap( "Build heap:" ,num,size); |
43 | } |
44 | } |
45 | |
46 | // 对该数进行下滤操作,直到该数比左右节点都小就停止下滤 |
47 | void PercolateDown( int num[] , int index, int size) { |
48 | int min; // 设置最小指向下标 |
49 | while (index * 2 + 1<size) { // 如果该数有左节点,则假设左节点最小 |
50 | min = index * 2 + 1; // 获取左节点的下标 |
51 | if (index * 2 + 2<size) { // 如果该数还有右节点 |
52 | if (num[min] > num[index * 2 + 2]) { // 就和左节点分出最小者 |
53 | min = index * 2 + 2; // 此时右节点更小,则更新min的指向下标 |
54 | } |
55 | } |
56 | // 此时进行该数和最小者进行比较, |
57 | if (num[index] < num[min]) { // 如果index最小, |
58 | break ; // 停止下滤操作 |
59 | } else { |
60 | Swap(num, index, min); // 交换两个数,让大数往下沉 |
61 | index = min; // 更新index的指向 |
62 | } |
63 | } // while |
64 | } |