时间: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;
}