排序算法总结

美团点评昨天晚上的笔试题充斥着排序。。故回顾一下:

先放一张神图:

能解决多数的选择题了。。话不多说,开怼

可能是题目做的少的原因,习惯性的做成可以自己查验的版本和刷题版本。


1.直接插入排序

为了实现N个数的排序,将后面N-1个数依次插入到前面已经排好的子序列中,假定刚开始第一个 

数是一个已排好序的子序列,那么经过N-1趟就能得到一个有序的序列。

//刷题版本
void InsertSort(vector<int>& nums){
    //少于两个元素的数组不用排序
    if (nums.size() < 2)return;

    //排序从第二个元素进行考察
    int n = nums.size();
    for (int i = 1; i < n; i++){
        int key = nums[i];
        int j;
        for ( j = i - 1; j >= 0&& key<nums[j]; j--){
                nums[j + 1] = nums[j];
        }
        nums[j + 1] = key;
    }

}


#include <iostream>
#include <vector>
#include <list>

using namespace std;
void InsertSort(vector<int>& nums){
    //less than two nums,return
    if (nums.size() < 2)return; //defensive code

    //check the next num
    int n = nums.size();
    for (int i = 1; i < n; i++){
        int key = nums[i];
        int j;
        for ( j = i - 1; j >= 0&& key<nums[j]; j--){
                nums[j + 1] = nums[j];
        }
        nums[j + 1] = key;
    }

}

int main()
{vector<int> vec;
  vector<int> vec2;
  int tmp;
  while(cin>>tmp)
    vec.push_back(tmp);
  InsertSort(vec);
  for(int i=0;i<vec.size();++i)
    cout<<vec[i]<<endl;
  return 0;
}

2.希尔排序

希尔排序是插入排序的一种。是对直接插入排序算法的 改进该方法又称为缩小增量排序。比较相距一定间隔的元素,即形如L[i,i+d,i+2d,…,i+kd]的序列然后缩小间距,再对各分组序列进行排序。直到之比较相邻元素的最后一趟排序为止,即最后的间距为1。 图来自点击打开链接


做插入排序的时候,就是比较每一组的大小,然后更换位置,让小的到前面。

//希尔排序
void ShellSort(vector<int> & nums){

    int n = nums.size();
    int i, j, tmp;
    int  d = n / 2;
    while (d >= 1){
    //对每组进行直接插入排序
        for (int i = d; i < n; ++i){
            tmp = nums[i];
            for (j = i - d; j >= 0 && tmp < nums[j]; j -= d){
                nums[j + d] = nums[j];
            }
            nums[j + d] = tmp;

        }
        d /= 2;
    }
}

3.直接选择排序

对要排序的序列,选出关键字最小的数据,将它和第一个位置的数据交换,接着,选出关键字次小的 数据,将它与第二个位置上的数据交换。以此类推,直到完成整个过程 所以如果有n个数据,那么则需要遍历n-1遍。 

//选择排序
void SelectSort(vector<int> &nums){
    int n = nums.size();
    //只需n-1个位置需要考察,因为最后一个自然而然就放上了
    for (int i = 0; i < n - 1; i++){
        //每一趟我都是要去找最小的那个的序号
        int minx = nums[i];
        int cur = i;
        for (int j = i+1; j < n; j++){
            if (minx > nums[j]){
                cur = j;
            }
        }
        //交换
        swap(nums[cur], nums[i]);
    }
}
#include<bits/stdc++.h>
using namespace std;
void swap(vector<int> &a, int i, int j){
	int tmp = 0;
	tmp = a[i];
	a[i] = a[j];
	a[j] = tmp;
}
void selectionSort(vector<int> &a){
	for (int i = 0;i < a.size(); i++)
	{
		int minIndex = i;
		for (int j = i+1; j <a.size(); ++j)
		{
			minIndex = a[j] < a[minIndex] ? j : minIndex;
		}
		swap(a, i, minIndex);
	}
}

int main()
{
	vector<int> arr;
	int tmp;
	while (cin >> tmp){
		arr.push_back(tmp);
		int ch = getchar();
		if (ch=='\n')
		{
			break;
		}
	}
	selectionSort(arr);
	for (int i = 0; i < arr.size(); i++)
		printf(i ==0 ? "%d" : " %d", arr[i]);
	return 0;
}

4.堆排序

堆排序经常用于求一个数组中最大k个元素时。因为堆实际上是一个完全二叉树,所以用它可以用一维数组来表示。因为最大堆的第一位总为当前堆中最大值,所以每次将最大值移除后,调整堆即可获得下一个最大值,通过一遍一遍执行这个过程就可以得到前k大元素,或者使堆有序。
在了解算法之前,首先了解在一维数组中节点的下标:
                               i节点的父节点 parent(i) = floor((i-1)/2)
                               i节点的左子节点 left(i) = 2i + 1
                               i节点的右子节点 right(i) = 2i + 2
【步骤】:
1.构造最大堆(Build Max Heap):首先将当前元素放入最大堆下一个位置,然后将此元素依次和它的父节点比较,如果大于父节点就和父节点交换,直到比较到根节点。重复执行到最后一个元素。
2.最大堆调整(Max Heapify):调整最大堆即将根节点移除后重新整理堆。整理方法为将根节点和最后一个节点交换,然后把堆看做n-1长度,将当前根节点逐步移动到其应该在的位置。

3.堆排序(HeapSort):重复执行2,直到所有根节点都已移除。

下图同样来自上面的图解大佬


void HeapAdjust(vector<int>&nums, int root, int size){

    //左孩子
    int leftChild = 2 * root + 1;
    //若有左孩子
    if (leftChild <= size - 1){
        //右孩子
        int rightChild = leftChild + 1;
        //若有右孩子
        if (rightChild <= size - 1){
            if (nums[leftChild] < nums[rightChild]){
                leftChild = rightChild;
            }
        }

        if (nums[root] < nums[leftChild]){
            swap(nums[root], nums[leftChild]);
            HeapAdjust(nums, leftChild, size);
        }
    }
}
void HeapSort(vector<int>& nums){

    int size = nums.size();
    for (int i = size / 2 - 1; i >= 0; --i){

        HeapAdjust(nums, i, size);
    }

    for (int i = size - 1; i>0; --i){

        swap(nums[0], nums[i]);
        HeapAdjust(nums, 0, i);
    }
}

5.冒泡排序

冒泡排序是最简单粗暴的排序方法之一。它的原理很简单,每次从左到右两两比较,把大的交换到后面,每次可以确保将前M个元素的最大值移动到最右边。

//从前往后遍历,保证排好序的放到右侧
void BubbleSort_from_0(vector<int>& nums){
    int n = nums.size();
    //一共进行n-1趟
    for (int i = 0; i < n - 1; i++){
        //注意j的起点和终点
        for (int j = 1; j < n - i; j++){
            if (nums[j] < nums[j - 1]){
                swap(nums[j], nums[j - 1]);
            }
        }
    }
}

//从后往前遍历,保证排好序的放到左侧
void BubbleSort_from_n(vector<int>& nums){

    int n = nums.size();
    //一共进行n-1趟
    for (int i = 0; i < n - 1; i++){
        //注意j的起点和终点
        for (int j = n-1; j >i; j--){
            if (nums[j] < nums[j-1]){
                swap(nums[j], nums[j - 1]);
            }

        }

    }

}
递归法实现冒泡。
#include <iostream>
using namespace std;

void BilateralBubbleSort(int*  , int,int );
int main() 
{ 	
    int data[]={10,9,13,7,32,5,2};
    BilateralBubbleSort(data,0,6);
    for(int i=0;i<7;i++)
        cout<<dec<<data[i]<<"  ";
        cout<<"\n";
    return 0;
} 

void BilateralBubbleSort(int *a, int first, int last) 
{
    if (first >= last) //退出条件
    {                          
         return;
    }

    int i = first;
    int j = last;
    
    int temp = a[first];    
    while (i != j) 
    {                            
        while (i<j && a[j]>=temp)  
        {
            j--;
        }
        a[i] = a[j];
        while (i<j && a[i]<=temp) 
        {
            i++;
        }
        a[j] = a[i];
    }
    a[i] = temp;

    //递归,有点像快速排序,就是中间值放在临时变量而不是数组尾部
    BilateralBubbleSort(a, first, i-1);     
    BilateralBubbleSort(a, i+1, last);
  
}

自己最近听左神的课开始,写的代码

#include<bits/stdc++.h>
using namespace std;
void swap(vector<int> &a, int i, int j){
  int tmp = 0;
  tmp = a[i];
  a[i] = a[j];
  a[j] = tmp;
}
void bubbleSort1(vector<int> &a){
  for (int end = a.size() - 1; end > 0; end--)
  {
    for (int i = 0; i < end; ++i)
    {
      if (a[i] > a[i + 1])
      {
        swap(a, i, i + 1);
      }
    }
  }
}
vector<int> bubbleSort2(vector<int> &ivec)
{
  const int  COUNT = ivec.size();
  for (int i = 1; i < COUNT; i++)
  {
    for (int j = 0; j < COUNT - i; ++j)
    {
      if (ivec[j] > ivec[j + 1])
      {
        swap(ivec, j, j + 1);
      }
    }
  }
  return ivec;
}
int main()
{
  vector<int> arr;
  int tmp;
  while (cin >> tmp){
    arr.push_back(tmp);
    int ch = getchar();
    if (ch=='\n')
    {
      break;
    }
  }
  //vector<int> arr2 = bubbleSort(arr);
  //for (vector<int>::iterator iter = arr2.begin();
  //  iter != arr2.end(); ++iter)
  //{
  //  cout << *iter << endl;
  //}
  bubbleSort1(arr);
  for (int i = 0; i < arr.size(); i++)
    printf(i ==0 ? "%d" : " %d", arr[i]);
  return 0;
}


6.快速排序

快速排序也是利用分治法实现的一个排序算法。快速排序和归并排序不同,它不是一半一半的分子数组,而是选择一个基准数,把比这个数小的挪到左边,把比这个数大的移到右边。然后不断对左右两部分也执行相同步骤,直到整个数组有序。


步骤
1.用一个基准数将数组分成两个子数组
2.将大于基准数的移到右边,小于的移到左边
3.递归的对子数组重复执行1,2,直到整个数组有序

void quick_sort(vector<int> &nums, int b, int e, vector<int> &temp)
{
    int m = (b + e) / 2;
    if (m != b) {
        int lb = b, rb = e - 1;

        for (int i = b; i < e; i++) {
            if (i == m)
                continue;
            if (nums[i] < nums[m])
                temp[lb++] = nums[i];
            else
                temp[rb--] = nums[i];
        }
        temp[lb] = nums[m];
        
        for (int i = b; i < e; i++)
            nums[i] = temp[i];
        
        quick_sort(nums, b, lb, temp);
        quick_sort(nums, lb + 1, e, temp);
    }
}

//解法二不要辅助空间
void quick_sort(vector<int> &nums, int b, int e)
{
    if (b < e - 1) {
        int lb = b, rb = e - 1;
        while (lb < rb) {
            while (nums[rb] >= nums[b] && lb < rb)
                rb--;
            while (nums[lb] <= nums[b] && lb < rb)
                lb++;
            swap(nums[lb], nums[rb]);
        }
        swap(nums[b], nums[lb]);
        quick_sort(nums, b, lb);
        quick_sort(nums, lb + 1, e);
    }
}
int Partion_from_LR(vector<int>&nums, int left, int right){
    //取左元素作为哨兵
    int key = nums[left];
    while (left < right){
        //一开始认为左侧位置是空的,所以从右侧找一个数来填充它
        while (left < right && nums[right] >= key)--right;
        nums[left] = nums[right];
        //轮到从左侧找一个合适的数来填充右侧right的空缺
        while (left < right&& nums[left] < key)++left;
        nums[right] = nums[left];
    }
    //最后把中间的空缺位置放上key
    nums[left] = key;
    //返回最终位置
    return left;

}

int Partion_from_L(vector<int>&nums, int left, int right){
    //从一侧开始的一般取另一侧的端点为key.因此取右侧端点为key
    int key = nums[right];
    //设置两个指针 i ,left
    int i = left - 1;
    while (left <=right){
        //保证i~left(不包含i)之间的数都比key大,0~i(含i)的数比key小
        if (nums[left] <= key){
            ++i;
            swap(nums[left], nums[i]);
        }
        ++left;
    }
    return i;
}

void QuickSort(vector<int> &nums, int left, int right){
    //递归法
    if (left < right){
        //int pos = Partion_from_L(nums, left, right);
        int pos = Partion_from_LR(nums, left, right);
        QuickSort(nums, left, pos - 1);
        QuickSort(nums, pos + 1, right);
    }
}

7.归并排序

归并排序是采用分治法(Divide and Conquer)的一个典型例子。这个排序的特点是把一个数组打散成小数组,然后再把小数组拼凑再排序,直到最终数组有序。
【步骤】
1.把当前数组分化成n个单位为1的子数组,然后两两比较合并成单位为2的n/2个子数组

2.继续进行这个过程,按照2的倍数进行子数组的比较合并,直到最终数组有序

合并排序采用分治算法,思路是将两个或以上的有序表合并为一个有序表,把待排序的序列分割成若干个子序列,每个子序列有序,然后再把子序列合并为一个有序序列。若将两个有序表合并成一个有序表,则成为2路合并排序。2-路归并即使将2个有序表组合成一个新的有序表。假定待排序表有n个元素,则可以看成是n个有序的字表,每个子表长度为1,然后两两归并…不停重复,直到合并成一个长度为n的有序列表为止。Merge()函数是将前后相邻的两个有序表归并为一个有序表,设A[low…mid]和A[mid+1…high]存放在同一顺序表的相邻位置上,先将他们复制到辅助数组B中,每次从对应B中的两个段取出一个元素进行比较,将较小者放入A中。 

//归并排序
//将两个有序的序列nums[low...mid] nums[mid...hight]合并
void mMerge(vector<int>&nums, int left, int mid, int right){
    int k = right - left + 1;
    vector<int> tmpArray(k);
    int p = right, q = mid;
    while (p > mid&& q >= left){
        if (nums[p] > nums[q]){
            tmpArray[--k] = nums[p--];
        }
        else{
            tmpArray[--k] = nums[q--];
        }
    }
    while (p > mid){
        tmpArray[--k] = nums[p--];
    }
    while (q >= left){
        tmpArray[--k] = nums[q--];
    }

    k = right - left + 1;
    for (int i = 0; i < k; i++){

        nums[left + i] = tmpArray[i];
    }

}
//折半法,即分治思想
void mymergeSort(vector<int>& nums, int left, int right){
    if (left < right){
        int mid = left + (right - left) / 2;
        mymergeSort(nums, left, mid);
        mymergeSort(nums, mid + 1, right);
        mMerge(nums, left, mid, right);
    }
}
void MergeSort(vector<int>& nums){
    int n = nums.size();
    mymergeSort(nums, 0, n - 1);
}

自己写的从小数开始进入外排的代码,包括对l+i的理解
#include<bits/stdc++.h>
using namespace std;
void mMerge(vector<int>& nums, int left, int mid, int right){
	int i = 0;
	vector<int> help(right-left+1);
	int p = left, q = mid + 1;
	while (p<=mid&&q<=right)
	{
		help[i++] = nums[p] < nums[q] ? nums[p++] : nums[q++];
	}
	while (p<=mid)
	{
		help[i++] = nums[p++];
	}
	while (q<=right)
	{
		help[i++] = nums[q++];
	}
	for (int i = 0; i < help.size();i++)
	{
		cout << "#######" << i << endl;
		nums[left+i] = help[i];
		cout <<"------"<< left << endl;
	}


}
//折半法,即分治思想  
void mymergeSort(vector<int>& nums, int left, int right){
	if (left < right){
		int mid = left + (right - left) / 2;
		mymergeSort(nums, left, mid);
		mymergeSort(nums, mid + 1, right);
		mMerge(nums, left, mid, right);
	}
}
void MergeSort(vector<int>& nums){
	int n = nums.size();
	mymergeSort(nums, 0, n - 1);
}
int main(){
	vector<int> arr;
	int temp;
	while (cin>>temp)
	{
		arr.push_back(temp);
		int ch = getchar();
		if (ch=='\n')
		{
			break;
		}

	}
	MergeSort(arr);
	for (int i = 0; i < arr.size();++i)
	{
		printf(i == 0 ? "%d" : " %d", arr[i]);
	}
	return 0;
} 


8.基数排序

它是一种非比较排序。它是根据位的高低进行排序,也就是先按照个位排序然后按照十位 

排序……依次类推。示例如下: 



/*基数排序*/
void RadixSort(vector<int> &array)
{
    int size = array.size();
    int bucket[10][10] = { 0 };//定义基数桶  
    int order[10] = { 0 };//保存每个基数桶之中的元素个数  
    int key_size = KeySize(array);//计算关键字位数的最大值  

    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,i;
        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(){
    vector<int> nums{25,54,34,922,134,778};
    RadixSort(nums);
    for (auto a : nums){
        cout << a << " ";
    }
    cout << endl;
    return 0;

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值