CMU硕士101-千奇百怪的排序算法

1,冒泡
遍历第i次时,找出当前数组的最i大值.
如何找出:不断和周边进行比较,大的上升,所以有冒泡的概念.
2,插入
遍历到第i个数字的时候,将当前数字和之前i-1个已近排序的进行比较,插入到第一个比当前数字大的数字前面.所以叫做插入排序
3,希尔
希尔排序是插入排序的延伸,插入排序的gap固定为1,希尔排序gap是1/2缩减.两个放在一起记忆.
4,选择
依然是遍历选择最小的与第一个数字交换位置,选择第二小的与第二个数字交换位置.当考虑到位置的时候index是不错的选择
5,快速
快排的概念是选择一个base,base当前所处位置为i,要做到比base小的都在左面,比base大的都在右面. base最终会在k位置。步遍历所在数组,从left->right的过程中遇到比base 大的与从right->left中遇到比base小的,两个数字互换.

6,归并
分治法.分和治,将数组逐步拆分,最终变成一个个只有一个元素的单数组。然后merge,对于merge可以参考成,将两个有序数组合并成一个长的有序数组. 遗留(单独写一个函数)

7,桶(哈希表和优先队列)

**冒泡排序**

/*
冒泡排序:递增
思路:
从左到右依次遍历,第一次遍历找到整个数组最小的number, 第二次找到第二小的....遍历N轮下来, 实现递增
*/
void BubbleSort(vector<int>&nums, int n) {
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            if (nums[j] < nums[i]) {
                swap(nums[i], nums[j]);
            }
        }
    }
}

**插入排序**

/*
插入排序(希尔排序的延伸)
思路:
每次将一个待排序的数字/单位, 按照关键字的大小插入到已近拍好的子序列/文件中,直至全部记录已经插入完成为止.
常见的有插入排序, 希尔排序, 二叉树查找排序, 图书馆排序.
进一步解释:就像打扑克,新牌会逐渐与旧牌比对,插在第一个大于key的前面。

https://www.cnblogs.com/clc2008/p/6847780.html
比如:
 step1:{12, 15, 9, 20, 6, 31, 24} 原有数组
 step2:[12], 15, 9, 20, 6, 31, 24
 step3:[12, 15 ], 9, 20, 6, 31, 24
 step4:[9,12, 15 ],  20, 6, 31, 24
 ....
*/
void InsertSort(vector<int>&nums, int n)
{
	for (int j = 1; j < n; j++) {
		int key = nums[j]; // 待排序的第一个元素
		int i = j - 1; // 表示已经排序的最后一个元素
		while(i >= 0 && key < nums[i]) {
			nums[i+1] = nums[i]; // 从后向前逐个比较key与已经排过序的数字的比较,如果比它小,当前字符向后移动一位
			i--;
		}
		nums[i+1] = key;
	}
}

**希尔排序**

希尔排序


希尔排序
/*
希尔排序:
思路:与插入排序类似,不同之处在于,会优先比较距离较远的元素,缩小增量排序.
先取一个小于len的整数d1作为第一个增量, 将文件全部分为d1个组,每个组len/d1个元素.因此每个的间隔都是d1[i, i + d1].
第一个组[1, 1 + d1]
第二个组[2, 2 + d1]
在各个组内部进行插入排序(当前数字逐个和前面的数字比较,插入到第一个比它大的数字中, 所以插入排序的前提就是前面是有序数组)
然后取步长d2, 再将数组分成d2个组, 每个组len/d2个元素.因此每个的间隔都是d2[i, i + d2, i + 2d2]
插入排序
step1:取步长
step2:插入排序
*/

void ShellSort(vector<int> &nums, int len)
{
	int i, j, gap;
	for (int gap = len / 2; gap > 0; gap /= 2) {
		// 一共有gap个组
		for (int i = 0; i < len; i++) {
			for (int j = i + gap; j < len; j += gap) {
				// 进行插入排序, j是当前值, 不断的插入之前的原有队列中
				if(nums[j] >= nums[i]) { // 说明是希望从大到小排列
					continue;
				}
				int tmp = nums[j];
				int k = j - gap;
				while(k >= 0 && nums[k] < tmp) {
					nums[k+gap] = nums[k];
					k-=gap;
				}
				nums[k + gap] = tmp;
			}
		}
	}
}

**选择排序**

/*
选择排序(从小到大)
思路:从头到尾遍历,选择最小的数字与第一个数字互换位置,然后再遍历,选择第二小的与第二个遍历.
从左到右遍历数组, 选择最小的数字与第一个数字互换位置, 选择第二小的数字与第二个数字互换位置.....是一个选择的过程.
*/

void SelectSort(vector<int>&nums, int n)
{
	for (int i = 0; i < n; i++) {
		int minIndex = i; // 这是很巧的一点, 使用了index而不是直接使用minNum. 方便后面的互换.
		for (int j = i + 1; j < n; j++) {
			if (nums[j] < nums[minIndex]) {
				minIndex = j;
			}
		}
		swap(nums[i], nums[minIndex]);
	}
}

**快速排序**

/*
快速排序
思路:
遍历nums到i,达到一个目标,nums[i]最终会放在k位置
int base = nums[i];
k左面的都是小于base, k右面的都是大于base,
从left向右遍历,遇到大于base, 
从right向左遍历,遇到小于base和上面的交换位置。while的条件是left<=right
然后base放在left=right的k位置。
递归QuickSort(nums, left + 1, k)
QuickSort(nums, k, right-1)
*/


//快速排序(从小到大)
void quickSort(int left, int right, vector<int>& arr)
{
	if(left >= right)
		return;
	int i, j, base, temp;
	i = left, j = right;
	base = arr[left];  //取最左边的数为基准数
	while (i < j)
	{
		while (arr[j] >= base && i < j)
			j--;
		while (arr[i] <= base && i < j)
			i++;
		if(i < j)
		{
			temp = arr[i];
			arr[i] = arr[j];
			arr[j] = temp;
		}
	}
	//基准数归位
	arr[left] = arr[i];
	arr[i] = base;
	quickSort(left, i - 1, arr);//递归左边
	quickSort(i + 1, right, arr);//递归右边
}

**归并排序**

/*
归并排序
思路:归并排序是建立在归并操作上的一种算法, 对序列中的元素进行逐层对半, 然后从最小分组开始比较排序, 合并成一个大的分组, 逐层进行,最终所有的元素都是有序的. 所以有两个操作, 分裂+排序.
归并排序采用的是分治法.(divide and conquer)
将两个有序数列合并:
比较两个数列的第一个数, 谁小取谁, 若有数列为空, 直接将另一个数列中的数据依次取出即可.


// 在写归并排序之前先写一下将两个有序数组AB合并到C中. 可以参考下面的Merge.

如何从上面的排序中推到出归并排序呢,将数组拆分成A和B两个数组,然后再将AB内部各自划分成两个数组,当分出来的小组内部只有一个数据的时候,认为小组内部已经达到了有序,然后合并相邻的两个数组。
这样就可以通过递归的分解数列,合并数列完成了归并排序
*/

第一步:建立新的数组,其大小为两个已经排序序列之和,用来存放合并后的序列。
第二步:设定两个index分别为两个已经排序序列的起始位置。firstIndex和secondIndex
第三步:比较两个index所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置。
重复步骤三,直到某一指针超出序列尾。
第四步:将另一序列剩下的所有元素直接复制到合并序列尾部

```cpp
#include <iostream>
#include <String>
#include <string.h>
#include <vector>
#include <stack>
using namespace  std;
void Merge(vector<int> &numbers, int start, int mid, int end) {
    vector<int> temp(end - start + 1, 0);//第一步,申请空间,大小为两个排序序列之和
    int fistSectionIndex = start;			//第二步,设定两个待排序列的起始位置的索引
    int secondSectionIndex = mid + 1;
    int tempIndex = 0;	//所申请空间的索引

    while (fistSectionIndex <= mid && secondSectionIndex <= end) {	//直到两个序列中有一个到达终止位置
        if (numbers[fistSectionIndex] <= numbers[secondSectionIndex])
            temp[tempIndex++] = numbers[fistSectionIndex++];
        else
            temp[tempIndex++] = numbers[secondSectionIndex++];
    }

    while (fistSectionIndex <= mid)
        temp[tempIndex++] = numbers[fistSectionIndex++];

    while (secondSectionIndex <= end)
        temp[tempIndex++] = numbers[secondSectionIndex++];

    for (int j = 0; j < tempIndex; ++j)		//将合并且排序好的元素,复制到原来的数组中,释放临时数组空间
        numbers[start + j] = temp[j];
}


void MergeSort(vector<int> &numbers, int start, int end) {
    if (start >= end)
        return;

    int mid = (start + end) / 2;

    MergeSort(numbers, start, mid);		//递归排序numbers[start,mid](首先从上往下递归分解到最底层元素个数为1的情况)
    MergeSort(numbers, mid + 1, end);	//递归排序numbers[mid + 1,end](首先从上往下递归分解到最底层元素个数为1的情况)

    Merge(numbers, start, mid, end);	//然后递归的从下往上合并排序
}

int main() {
    vector<int> nums{ 5,6,1,8,3,4,9,7,2,3 };
    cout << "归并排序前:";
    for (int i = 0; i < 10; i++)
        cout << nums[i] << ' ';
    cout << endl;

    MergeSort(nums, 0, 9);

    cout << "归并排序后:";
    for (int i = 0; i < 10; i++)
        cout << nums[i] << ' ';
    cout << endl;
}

void Merge(vector&nums, int first, int mid, int last, vector temp)

{
int leftIndex = first;
int rightIndex = mid + 1;
int tempIndex = 0;

while (leftIndex <= mid && rightIndex <= last) {
    if (nums[leftIndex] < nums[rightIndex]) {
		temp[tempIndex++] = nums[leftIndex++];
	} else {
		temp[tempIndex++] = nums[rightIndex++];
	}
} 

while (leftIndex <= mid) {
	temp[tempIndex++] = nums[leftIndex++];
}	

while (rightIndex <= last) {
	temp[tempIndex++] = nums[rightIndex++];
}

for (leftIndex = 0; leftIndex < tempIndex; leftIndex++) {
	nums[first + leftIndex] = temp[leftIndex];
}

}

void Divide(vector&nums, int first, int last, vector&temp)

{
if (first >= last) {
return;
}
int mid = (first + last) / 2;

Divide(nums, first, mid, temp);    //左边有序

Divide(nums, mid + 1, last, temp); //右边有序

Merge(nums, first, mid, last, temp); //再将二个有序数列合并

}

bool MergeSort(vector&nums, int n)

{
vector

**桶排序**


/*
桶排序
347. 前 K 个高频元素
思路:
使用哈希表来统计每个字符出现的个数, 然后就是如何找出top K的元素.
更希望把map值倒过来从<char, int> 变成<int, char>, 最好还是排序的, 所以想到了priority_queue
*/
class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int,int> map;         //统计频次
        for(auto i:nums) map[i]++;
        
        priority_queue<pair<int,int>> que;  
        for(auto m:map){
            que.push(make_pair(m.second, m.first));     //优先队列(堆)按照第一项进行排序
        }

        vector<int> vec;
        for(int i=0;i<k;i++){
            vec.push_back(que.top().second);
            que.pop();
        }
        return vec;
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值