排序算法总结

时间:2019/04/09
文章总结了常用了几种排序算法,包括选择排序,冒泡排序,直接插入排序,快速排序,归并排序,希尔排序,堆排序,拓扑排序

选择排序O(n^2),O(1)

思路:每次从待排序的元素中选出一个最小(大)的元素作为已排好序数组的最后一个元素。

void select_sort(vector<int> array) {
	for (int i = 0; i < len; i++) {
		int min = i;
		for (int j = i; j < len; j++) {
			if (arr[j] < arr[min])  min = j;
		}
		if (i != min) swap(arr[i], arr[min]);
	}
}

冒泡排序O(n^2),O(1)

思路:每一趟排序中,将最大的元素通过交换放到最后面,进行N-1趟

void bubble_sort(vector<int> array) {
	int len = array.size();
	for (int i = 0; i < len - 1; i++) {
		for (int j = 0; j < len - i - 1; j++) {
			if (array[j] > array[j + 1])
				swap(array[j], array[j + 1]);
		}
	}
}

直接插入排序O(n^2),O(1)

思路:每一步将一个未排序元素插入到已排序数组的合适位置

void insertSort(int arr[], int len) {
	for (int i = 0; i < len - 1; i++) {
		for (int j = i + 1; j > 0; j--) {
			if (arr[j] < arr[j - 1]) swap(arr[j], arr[j - 1]);
		}
	}
}

快速排序O(nlogn)

思路:每一趟排序将一个数字放到正确的位置,且保证它的左边都比他小,它的右边也都比它大。然后递归处理左边和右边。

int partition(vector<int>& array, int start, int end) { // & 非常重要
	int i = start, j = end;
	int num = array[start];
	while (i != j) {
		while (j > i && array[j] >= num) j--;  //这里比较的标准是当前需要排序的那个数字,在一趟排序中是固定的,而不是比较两个指针所指值的大小
		if (j > i) array[i++] = array[j];
		while (j > i && array[i] <= num) i++;
		if (j > i) array[j--] = array[i];
	}
	array[i] = num;
	return i;
}
void quick_sort(vector<int>& array, int start, int end) {  // 排序的起始位置也作为参数传入,写成闭区间比较好
	if (end - start <= 0) return;
	int pos = partition(array, start, end);
	quick_sort(array, start, pos - 1);
	quick_sort(array, pos + 1, end);
}

归并排序O(nlogn) O(n)

思路:先将整个序列二分到没一段只有1,然后不断递归合并相邻段,合并时保证两小段内有序
递归写法

void merge_core(vector<int> array, int start, int mid, int end) { // &可以不写
	int i = start, j = mid + 1;  //这里赋值要注意
	vector<int> tmp;
	while (i <= mid && j <= end) {
		if (array[i] < array[j]) tmp.push_back(array[i++]);
		else tmp.push_back(array[j++]);
	}
	while (i <= mid) tmp.push_back(array[i++]);
	while (j <= end) tmp.push_back(array[j++]);
	for (int i = start; i <= end; i++) {
		array[i] = tmp[i - start];  // tmp的只要注意减start 
	}
}

void merge_sort(vector<int> array, int start, int end) {
	if (end - start <= 0) return;
	int mid = (start + end) >> 1;
	merge_sort(array, start, mid); // 这里是一个二分,容易写出bug。若左区间不包含mid,则会出死循环。参考二分模板
	merge_sort(array, mid + 1, end);
	merge_core(array, start, mid, end);
}

非递归写法

void merge_core(int array[], int start, int mid, int end) {
	vector<int> tmp;
	int i = start;
	int j = mid;
	int s = 0;
	while (i < mid && j < end) {
		if (array[i] < array[j]) tmp.push_back(array[i++]);
		else tmp.push_back(array[j++]);
	}
	while (i < mid) tmp.push_back(array[i++]);
	while (j < end) tmp.push_back(array[j++]);
	int k = start;
	for (auto x : tmp) {  
		array[k++] = x; 
	}
}

void merge_sort(int arr[], int len) {
	int j = 1;
	while (j < len) {
		int i = 0;
		while (i + j <= len) {
			int tmp = 0;
			if (i + 2 * j <= len) tmp = i + 2 * j;
			else tmp = len;
			merge_core(arr, i, i + j, tmp);
			i += 2 * j;
		}
		j *= 2;
	}
}

希尔排序O(nlogn)O(1)

思路: 把待排序数组按照一定的增量(N/2)分组,对每一一个分组直接插入排序,然后减小增量(N/2),重新排序,直到增量为1;

void shell_sort(int arr[], int len) {
	for (int i = len / 2; i >= 1; i /= 2) {
		for (int j = i; j < len; j++) {
			for (int k = j - i; k >= 0; k -= i) {
				if (arr[k] > arr[k + i]) swap(arr[k], arr[k + i]); //冒泡思想
			}
		}
	}
}

堆排序O(nlogn)O(1)

思路:先建大根堆,然后最后一个点和根堆交换,这时最大的元素已经排好了,重新将堆调整为大根堆,排好的最大元素可以不再参与调整和排序。当需要调整的堆只剩一个元素时,算法结束。

void adjust_heap(vector<int>& array, int s, int len) {
	int tmp = array[s];
	for (int i = 2 * s + 1; i < len; i = 2 * i + 1) {
		if (i + 1 < len && array[i] < array[i + 1]) i++; // 这里要判断++之后是否越界,不然会error
		if (array[i] > tmp) { //这个tmp是最小的了(首尾交换后的)
			array[s] = array[i];
			s = i;
		}
	}
	array[s] = tmp;
}

void heap_sort(vector<int>& array){
	int len = array.size();
	for (int i = len / 2; i >= 0; i--) { //建大根堆
		adjust_heap(array, i, len - 1);
	}
	for (int i = len - 1; i >= 0; i--) {
		swap(array[0], array[i]);
		adjust_heap(array, 0, i);
	}
}

拓扑排序

主要用于图中。
将入度为0的点放到队列中,并将这个点抹去,即它的出度对应的点入度减1,然后再将入度为0的点加入到队列中,不断循环。若有点入度不能较小至0,说明有环。典型运用:course schdule
Leetcode: https://leetcode.com/problems/course-schedule/

bool canFinish(int numCourses, vector<pair<int, int>>& pre) {
        if (!pre.size()) return true;
        int m = pre.size();
        vector<int> in_degree(numCourses, 0);
        // vector<vector<int> > graph(numCourses, vector<int> (numCourses));  //ERROR
        vector<vector<int> > graph(numCourses);
        for (int i = 0; i < m; i++){
            in_degree[pre[i].first]++;
            graph[pre[i].second].push_back(pre[i].first);
        }
        deque<int> dq;
        vector<bool> is_visited(numCourses, false);
        for (int i = 0; i < numCourses; i++){
            if (in_degree[i] == 0){
                dq.push_back(i);
                is_visited[i] = true;
            }           
        }

        while(dq.size()){
            int tmp = dq.front();
            dq.pop_front();
 
            for (int i = 0; i < graph[tmp].size(); i++){
                in_degree[graph[tmp][i]] --;
                if (in_degree[graph[tmp][i]] == 0){
                    dq.push_back(graph[tmp][i]);
                    is_visited[graph[tmp][i]] = true;
                }
            }
        }
        for (int i = 0; i < numCourses; i++){
            if (is_visited[i] == false) return false;
        }
        return true;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值