排序问题
虽临近期末考试,但仍要总结一下各种排序算法。
以下默认全部为升序排序(从小到大)
如果一种排序算法能够保证同值元素之间的相对次序不变则称为稳定排序
选择排序(selection sort)
选择排序的方法十分简单。第i次搜索,搜索从第i个元素到第n个元素中的最小值,将其放到第i的位置上。(将原来第i位置的元素与后面的最小值进行交换即可)
依次循环,循环到最后一个元素,即可终止算法。
算法思路的核心就是每次选择最小的,放到前面对应的位置。这样整个序列就分为前面排好序的序列和后面待排序的序列。
很明显复杂度在
O
(
n
2
)
O(n^2)
O(n2)级别上。
实现代码
void select_sort(int * element,int n){
for(int i=0;i<n;i++){
int min=i; //give the first search index to min
for(int j=i;j<n;j++){
if(element[min]>element[j]){
min=j;
}
}
swap(element[min],element[i]); //let the minnest value to ith
}
}
插入排序(insertion sort)
插入排序的思路与选择排序类似。需要从头到尾扫描一次,每到一个元素,就搜索它前面已经有序的序列中寻找它应当在的位置,继续保证前边序列有序。
选择排序是搜索后方最小的元素直接放到前面的某个位置,此时这个值就在这个位置,在后续搜索过程中也不会变化。
而插入排序,是在遍历序列的过程中,始终保证到搜索点前端序列是有序的。这样直到最后一个元素,插入前n-1个序列中,就保证了前n个元素序列是有序的。
实现代码
void insert_sort(int * element,int n){
for(int i=0;i<n;i++){
int index=i; //search from index
while(index>0&&element[index-1]>element[index]){
swap(element[index-1],element[index]);
index--; //find the previous node
}
}
}
冒泡排序(bubble sort)
冒泡排序每次从头开始遍历整个序列,每一次遍历,从开始的元素依次向后比较,两两进行比较,将大的元素放到后边。这样每一次循环都会将一个最大元素放到后面(第一次将最大的元素放到第n位置,第二次将次大的元素放到n-1的位置,依次进行),这就就保证后面的序列是有序的,当执行n次循环时,那倒数n个的序列都有序,即整个序列有序
这个算法相当于插入排序的一个倒序过程。
插入排序保证前面是有序的
冒泡排序保证后面是有序的。而且还保证了每一次放到第i位置的元素就是第i大,之后的循环不会影响它的位置了。(这就与选择排序部分相似)
实现代码
void bubble_sort(int * element,int n){
for(int i=0;i<n;i++){//this is the times
for(int j=0;j<n-1-i;j++){
if(element[j]>element[j+1]) swap(element[j],element[j+1]);
}
}
}
基数排序(radix sort)
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。
首先确定需要比较的位数(通过最大值确定),然后依次从低位进行比较。每个位数的比较通过计数排序获得。
计数排序中最巧妙地部分在于
//very important
for(int i=1;i<10;i++){
record[i]+=record[i-1];
}
通过累计求和可以很方便的确定第i个元素应当在有序数组的中的位置。
实现代码
void count_sort1(int * element,int n,int exp){
int * result=new int [n];
int record[10]; //record the count
for(int i=0;i<10;i++) record[i]=0;
//get the record
for(int i=0;i<n;i++){
record[(element[i]/exp)%10]++;
}
//very important
for(int i=1;i<10;i++){
record[i]+=record[i-1];
}
//cause the result list
for(int i=n-1;i>=0;i--){
result[record[(element[i]/exp)%10]-1]=element[i];
record[(element[i]/exp)%10]--;
}
//make the result to element
for(int i=0;i<n;i++){
element[i]=result[i];
}
}
void radix_sort(int * element,int n){
int exp; //record the point
//get the max number
int maxx=element[0];
for(int i=1;i<n;i++){
if(maxx<element[i]) maxx=element[i];
}
//count sorting according to each order
for(exp=1;maxx/exp>0;exp=exp*10){
count_sort1(element,n,exp);
}
return;
}
桶排序(bucket sort)
堆排序(heap sort)
堆排序主要用最小堆的性质,每次取出最小元素放到有序数组中。
当然也可以直接在element元素中手动实现最大堆(先进行初始化,再依次取出最大元素放到(数组末尾
−
i
-i
−i)的位置,直到最后整个数组就是从小到大的有序数组。)
void stack_sort(int* element,int n){
priority_queue<int,vector<int>,greater<int>> que;
for(int i=0;i<n;i++)
que.push(element[i]);
int index=0;
while(!que.empty()){
element[index++]=que.top();
que.pop();
}
return;
}
计数排序(count sort)
将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
void count_sort(int * element,int n,int maxx){
int* record=new int [maxx+1];
//inital
for(int i=0;i<=maxx;i++){
record[i]=0;
}
//counting
for(int i=0;i<n;i++){
record[element[i]]++;
}
//cause new list
int index=0;
for(int i=0;i<=maxx;i++){
while(record[i]!=0){
element[index++]=i;
record[i]--;
}
}
delete [] record;
}
快速排序(quick sort)
快速排序的主要思想就是每次将待排序的序列分成两部分,前一部分的序号都小于后一部分,之后分别对着两部分进行排序。
详细图解:快速排序
首先需要挑出一个作为基准,重新排序序列,把所有比基准小的数放到基准的前面,把所有比基准大的数放到基准的后边。
完成操作后,即将排序分成了两部分。然后递归进行即可。
template<typename T>
void Quick_sort <T> ::quicksort(int left,int right){
if(left>right) return;
int i,j;
i=left;j=right;
T temp=element[left]; //start
while(i!=j){
while(element[j]>=temp&&i<j){j--;} //find the element smaller than temp at right series
while(element[i]<=temp&&i<j){i++;} //find the element bigger than temp at left series
if(i<j){
T record=element[i];
element[i]=element[j];
element[j]=record;
}
}
//change the middle and the start element
element[left]=element[i];
element[i]=temp;
quicksort(left,i-1);
quicksort(i+1,right);
}