不知道什么原因,重新复习了一下归并排序,本来想写点东西,在网上看到了好多这方面的文章,都还不错。所以就不自己写了,转载一下吧。
其他的没看,只看了归并排序的。tempArray临时内存放的位置非常好,只申请一次,总申请内存也非常耗时的。
一.插入排序(insert sorting)
最差情况下,直接插入排序的最大时间代价为θ(n²),最小时间代价为θ(n),平均时间代价为θ(n²)。
- #include <iostream>
- using namespace std;
- /*交换函数,作用是交换数组中的两个元素的位置*/
- void swap(int array[],int i,int j)
- {
- int tmp=array[i];
- array[i]=array[j];
- array[j]=tmp;
- }
- /*插入排序*/
- void InsertSort(int array[],int n)
- {
- for(int i=1;i<n;i++)
- {
- for(int j=i;j>0;j--)
- {
- if(array[j]>array[j-1])
- swap(array,j,j-1);
- else
- break;
- }
- }
- }
- int main()
- {
- int array[5]={3,1,2,5,4};
- InsertSort(array,5);
- for(int i=0;i<5;i++)
- cout<<array[i]<<" ";
- cout<<endl;
- return 0;
- }
二.冒泡排序(bubble sorting)
冒泡排序的最大时间代价,最小时间代价和平均时间代价均为θ(n²)。
- #include <iostream>
- using namespace std;
- /*交换函数,作用是交换数组中的两个元素的位置*/
- void swap(int array[],int i,int j)
- {
- int tmp=array[i];
- array[i]=array[j];
- array[j]=tmp;
- }
- /*冒泡排序*/
- void BubbleSort(int array[],int n)
- {
- for(int i=0;i<n-1;i++)
- {
- for(int j=n-1;j>i;j--)
- {
- if(array[j]<array[j-1])
- swap(array,j,j-1);
- }
- }
- }
- int main()
- {
- int array[5]={3,1,2,5,4};
- BubbleSort(array,5);
- for(int i=0;i<5;i++)
- cout<<array[i]<<" ";
- cout<<endl;
- return 0;
- }
三.选择排序(selection sorting)
选择排序的最大时间代价,最小时间代价和平均时间代价均为θ(n²)。选择排序不依赖于原始数组的输入顺序。
- #include <iostream>
- using namespace std;
- /*交换函数,作用是交换数组中的两个元素的位置*/
- void swap(int array[],int i,int j)
- {
- int tmp=array[i];
- array[i]=array[j];
- array[j]=tmp;
- }
- /*选择排序*/
- void SelectionSort(int array[],int n)
- {
- for(int i=0;i<n-1;i++)
- {
- int smallest=i;
- for(int j=i+1;j<n;j++)
- {
- if(array[smallest]>array[j])
- smallest=j;
- }
- swap(array,i,smallest);
- }
- }
- int main()
- {
- int array[5]={3,1,2,5,4};
- SelectionSort(array,5);
- for(int i=0;i<5;i++)
- cout<<array[i]<<" ";
- cout<<endl;
- return 0;
- }
四.shell sorting
增量为2的shell排序的时间代价可以达到θ(n的3/2次方),有的增量可以达到θ(n的7/6次方),很接近θ(n)。
- #include <iostream>
- using namespace std;
- /*交换函数,作用是交换数组中的两个元素的位置*/
- void swap(int array[],int i,int j)
- {
- int tmp=array[i];
- array[i]=array[j];
- array[j]=tmp;
- }
- /*希尔排序*/
- void ShellSort(int array[],int n)
- {
- for(int delta=n/2;delta>0;delta/=2)
- {
- for(int i=0;i<delta;i++)
- {
- for(int j=i+delta;j<n;j+=delta)
- {
- for(int k=j;k>0;k-=delta)
- {
- if(array[k]<array[k-1])
- swap(array,k,k-1);
- }
- }
- }
- }
- }
- int main()
- {
- int array[8]={6,8,7,3,1,2,5,4};
- ShellSort(array,8);
- for(int i=0;i<8;i++)
- cout<<array[i]<<" ";
- cout<<endl;
- return 0;
- }
五.快速排序(quick sorting)
快速排序的最大时间代价为θ(n²),最小时间代价为θ(n*logn),平均时间代价为θ(n*logn)。注意:快速排序是一种不稳定的排序方式,其性能依赖于原始数组的有序程度,更进一步分析,就是依赖与轴值元素的选择。快排的比较次数远多于移动次数,所以主要考虑比较次数。
快排中,每一次比较可以确定一个轴值元素的位置。若p[m,q,n](q为轴值元素)。当然确定第一个轴值元素也是要比较(n-m-1)次。但第二个轴值元素,第三个轴值元素就要进行(q-m-1)和(n-q-1)次比较。如果q的值若为m或n,快速排序就退化成冒泡排序了,快排就没有什么优势了。
- #include <iostream>
- using namespace std;
- /*交换函数,作用是交换数组中的两个元素的位置*/
- void swap(int array[],int i,int j)
- {
- int tmp=array[i];
- array[i]=array[j];
- array[j]=tmp;
- }
- /*将轴值放到数组的适当的位置*/
- int partition(int array[],int left,int right)
- {
- int mid=(left+right)/2;
- int tmp=array[mid];
- swap(array,mid,right);
- int i=left;
- int j=right;
- while(1)
- {
- /*i指针向右移动,直到找到一个大于轴值的值*/
- while(1)
- {
- /*如果i与j相遇则确定轴值位置,将其返回*/
- if(i==j)
- {
- array[i]=tmp;
- return i;
- }
- if(array[i]>tmp)
- {
- array[j]=array[i];
- j--;
- break;
- }
- i++;
- }
- /*j指针向左移动,直到找到一个小于轴值的值*/
- while(1)
- {
- /*如果i与j相遇则确定轴值位置,将其返回*/
- if(i==j)
- {
- array[j]=tmp;
- return j;
- }
- if(array[j]<tmp)
- {
- array[i]=array[j];
- i++;
- break;
- }
- j--;
- }
- }
- }
- /*快速排序*/
- void quickSort(int array[],int left,int right)
- {
- if(right<=left)
- return;
- int pivot=partition(array,left,right);
- quickSort(array,left,pivot-1);
- quickSort(array,pivot+1,right);
- }
- int main()
- {
- int array[8]={6,8,7,3,1,2,5,4};
- quickSort(array,0,7);
- for(int i=0;i<8;i++)
- cout<<array[i]<<" ";
- cout<<endl;
- return 0;
- }
六.归并排序(merge sorting)
归并排序的最大时间代价,最小时间代价和平均时间代价均为θ(n*logn)。归并排序不依赖于原始数组的有序程度。
- #include <iostream>
- using namespace std;
- /*归并过程--将两个有序数组合并成一个有序数组*/
- void merge(int array[],int tempArray[],int left,int right,int middle)
- {
- int index1=left;
- int index2=middle+1;
- for(int i=left;(index1<=middle)&&(index2<=right);i++)
- {
- if(array[index1]<array[index2])
- tempArray[i]=array[index1++];
- else
- tempArray[i]=array[index2++];
- }
- while(index1<=middle)
- tempArray[i++]=array[index1++];
- while(index2<=right)
- tempArray[i++]=array[index2++];
- for(i=left;i<=right;i++)
- array[i]=tempArray[i];
- }
- /*两路归并排序--array[]为待排数组,tempArray[]为临时数组,left和right分别是数组两端*/
- void mergeSort(int array[],int tempArray[],int left,int right)
- {
- if(left<right)
- {
- int middle=(left+right)/2;
- mergeSort(array,tempArray,left,middle);
- mergeSort(array,tempArray,middle+1,right);
- merge(array,tempArray,left,right,middle);
- }
- }
- int main()
- {
- int array[8]={6,8,7,3,1,2,5,4};
- int tempArray[8];
- mergeSort(array,tempArray,0,7);
- for(int i=0;i<8;i++)
- cout<<array[i]<<" ";
- cout<<endl;
- return 0;
- }
七.堆排序(heap sorting)
堆排序的最大时间代价,最小时间代价和平均时间代价均为θ(n*logn)。堆排序和归并排序一样,不依赖于原始数组的有序程度。
HeapSorting.cpp
- #include <iostream>
- #include "MaxHeap.h"
- using namespace std;
- /*最大堆排序函数*/
- void heapSort(int array[],int n)
- {
- MaxHeap max_heap=MaxHeap(array,n);
- /*删除堆的最大值(堆顶),即每次将最大值与数组的最后一个元素交换位置*/
- for(int i=0;i<7;i++)
- max_heap.removeMax();
- }
- int main()
- {
- int array[8]={4,3,7,1,2,8,5,6};
- heapSort(array,8);
- for(int i=0;i<8;i++)
- cout<<array[i]<<" ";
- cout<<endl;
- return 0;
- }
MaxHeap.h
- #include <iostream>
- using namespace std;
- /*最大堆定义*/
- class MaxHeap
- {
- private:
- int size; //最大堆的元素数目
- int * array; //最大堆数组的首地址指针
- public:
- MaxHeap(int array[],int n); //用已有数组初始化一个最大堆
- void buildHeap(); //构建最大堆
- void siftDown(int index); //向下筛选法
- void swap(int index1,int index2); //交换位置为index1与index2的元素
- void removeMax(); //删除堆顶的最大值--与数组最后一个元素交换位置并重新构建最大堆
- int leftChild(int index); //返回左孩子的位置
- int rightChild(int index); //返回右孩子的位置
- };
MaxHeap.cpp
- #include <iostream>
- #include "MaxHeap.h"
- using namespace std;
- /*最大堆成员函数实现*/
- MaxHeap::MaxHeap(int array[],int n)
- {
- this->array=array;
- size=n;
- buildHeap();
- }
- void MaxHeap::buildHeap()
- {
- for(int i=size/2-1;i>=0;i--)
- siftDown(i);
- }
- void MaxHeap::siftDown(int index)
- {
- int max_index=leftChild(index);
- while(max_index<size)
- {
- if(max_index<size-1&&array[rightChild(index)]>array[max_index])
- max_index++;
- if(array[index]>array[max_index])
- break;
- swap(index,max_index);
- index=max_index;
- max_index=leftChild(index);
- }
- }
- void MaxHeap::swap(int index1,int index2)
- {
- int temp=array[index1];
- array[index1]=array[index2];
- array[index2]=temp;
- }
- void MaxHeap::removeMax()
- {
- swap(0,size-1);
- size--;
- siftDown(0);
- }
- int MaxHeap::leftChild(int index)
- {
- return index*2+1;
- }
- int MaxHeap::rightChild(int index)
- {
- return index*2+2;
- }
八.基数排序(radix sorting)
基数排序的时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的比较性排序法。基数排序法是属于稳定性的排序。
- #include <iostream>
- using namespace std;
- /*计算关键字位数的最大值*/
- int KeySize(int array[],int size)
- {
- int key_size=1;
- for(int i=0;i<size;i++)
- {
- int temp=1;
- int n=10;
- while(array[i]/n>0)
- {
- temp++;
- n*=10;
- }
- key_size=(temp>key_size)?temp:key_size;
- }
- return key_size;
- }
- /*基数排序*/
- void RadixSort(int array[],int size)
- {
- int bucket[10][10]={0};//定义基数桶
- int order[10]={0};//保存每个基数桶之中的元素个数
- int key_size=KeySize(array,size);//计算关键字位数的最大值
- for(int n=1;key_size>0;n*=10,key_size--)
- {
- /*将待排序的元素按照关键值的大小依次放入基数桶之中*/
- for(int i=0;i<size;i++)
- {
- int lsd=(array[i]/n)%10;
- bucket[lsd][order[lsd]]=array[i];
- order[lsd]++;
- }
- /*将基数桶中的元素重新串接起来*/
- int k=0;
- for(i=0;i<10;i++)
- {
- if(order[i]!=0)
- {
- for(int j=0;j<order[i];j++)
- {
- array[k]=bucket[i][j];
- k++;
- }
- order[i]=0;
- }
- }
- }
- }
- int main()
- {
- int array[10]={73,22,93,43,55,14,28,65,39,81};
- int size=sizeof(array)/sizeof(int);
- RadixSort(array,size);
- for(int i=0;i<size;i++)
- cout<<array[i]<<" ";
- cout<<endl;
- return 0;
- }
下面我们来讨论一下各种排序算法的稳定度,稳定排序算法会依照相等的关键(换言之就是值)维持纪录的相对次序。也就是一个排序算法是稳定的,就是当有两个有相等关键的纪录R和S,且在原本的串列中R出现在S之前,在排序过的串列中R也将会是在S之前。
一般的方法:插入、交换、选择、合并等等。交换排序包含冒泡排序(bubble sort)和快速排序(quicksort)。选择排序包含shaker排序和堆排序(heapsort)。
当相等的元素是无法分辨的,比如像是整数,稳定度并不是一个问题。然而,假设以下的数对将要以他们的第一个数字来排序。
(4, 1) (3, 1) (3, 7) (5, 6)
在这个状况下,有可能产生两种不同的结果,一个是依照相等的键值维持相对的次序,而另外一个则没有:
(3, 1) (3, 7) (4, 1) (5, 6) (维持次序)
(3, 7) (3, 1) (4, 1) (5, 6) (次序被改变)
不稳定排序算法可能会在相等的键值中改变纪录的相对次序,但是稳定排序算法从来不会如此。不稳定排序算法可以被特别地时作为稳定。作这件事情的一个方式是人工扩充键值的比较,如此在其他方面相同键值的两个物件间之比较,就会被决定使用在原先资料次序中的条目,当作一个同分决赛。然而,要记住这种次序通常牵涉到额外的空间负担。
稳定的排序算法:
冒泡排序(bubble sort) — O(n2)
鸡尾酒排序 (Cocktail sort, 双向的冒泡排序) — O(n2)
插入排序 (insertion sort)— O(n2)
桶排序 (bucket sort)— O(n); 需要 O(k) 额外 记忆体
归并排序 (merge sort)— O(n log n); 需要 O(n) 额外记忆体
原地归并排序 — O(n2)
二叉树排序 (Binary tree sort) — O(n log n); 需要 O(n) 额外记忆体
基数排序 (radix sort)— O(n·k); 需要 O(n) 额外记忆体
不稳定的排序算法:
选择排序 (selection sort)— O(n2)
希尔排序 (shell sort)— O(n log n) 如果使用最佳的现在版本
Comb sort — O(n log n)
堆排序 (heapsort)— O(n log n)
Smoothsort — O(n log n)
快速排序 (quicksort)— O(n log n) 期望时间, O(n2) 最坏情况; 对於大的、乱数串列一般相信是最快的已知排序
一般来说:存在不相邻交换的排序算法是不稳定的,相邻交换的排序算法是稳定的;对于相邻交换的稳定排序算法,通过控制交换条件可以转换成不稳定排序算法;冒泡、插入、归并和基数排序是稳定的;选择、快速、希尔和堆排序是不稳定的。