目录
快速排序(基于交换)
特性
每次使一个数归位。不稳定
思想
每次partition划分数组为两部分并返回下标。随后递归地对左右序列快速排序。
代码
int Partition(int arr[],int left,int right) { int i = left; int j = right; int pivotindex = left; //基准坐标可以任意选 int pivotnum = arr[pivotindex]; //基准数 cout << "pivotnum = " << pivotnum << endl; while(i < j) //当i==j的时候,i和j的下标就是基准数的正确位置。 { while (i < j && arr[j] >= pivotnum) //先从后面往前找小于基准数 j--; arr[i] = arr[j]; //复制到前面i的位置,不用交换 while (i < j && arr[i] <= pivotnum) //再从前往后找大于基准数的值 i++; arr[j] = arr[i]; //复制到后面j的位置。 } arr[i] = pivotnum; //i,j的位置就是要找的基准数所在的位置。至此数组划分完毕。 return i; } void QuickSort(int arr[], int left,int right) { if (left == right) return; int index = Partition(arr, left, right); if(index>left) QuickSort(arr, left, index - 1); if(index<right) QuickSort(arr, index + 1, right); }
冒泡排序(交换排序)
思想:
两两比较,每次使最小的元素不断往上浮。
特点:
时间复杂度O(N)~O(N^2),空间复杂度为O(1)。稳定,每趟排序使得一个最小的元素归位,全局有序。
代码:
oid bubbleSort(int arr[], int len) { for (int i = 0; i < len-1; i++) { int flag = 0;//flag记录此次排序有没有交换数字 for (int j = len - 1; j >= i+1; j--)//从后往前冒泡 { if (arr[j] < arr[j - 1]) { swap(arr[j], arr[j - 1]); flag = 1; } } if (flag == 0) //如果本次排序没有交换数字,则表示全局有序。 return; } }
直接插入排序(插入排序)
思想:
将后面无序数组中的一个待排序的数插入到前面已排序的序列中去。找一个无序的数字,在前面有序的序列中找到插入的位置,移动元素,插入。
特点:
稳定,适用于链表。时间复杂度 O(N)~O(N^2),空间复杂度:O(1)。适用于基本有序或规模小的数组。
代码:
void InsertSort(int arr[], int len) { int i, j; for (i = 1; i < len; i++) //从第二个数字开始往前插入。 { int temp; //保存将要往前插入的数字。 if (arr[i] < arr[i - 1])//若这个数字本身和前面有序数组放一起是有序的,则跳过。 { j = i; temp = arr[i]; for (; j>0&&temp < arr[j-1]; j--) //从后面往前找插入的位置 { arr[j] = arr[j-1]; //直接覆盖 } arr[j] = temp; } } }
折半插入排序(插入排序)
思想:
相比于直接插入排序,折半插入排序将比较和移动操作分离。查找插入的位置用二分法。
特点:
减少了比较的次数,但移动的次数没变,时间复杂度仍未O(N^2)。
代码:
void InsertSort2(int arr[], int len) { int i, j; for (i = 1; i < len; i++) { int temp; if (arr[i] < arr[i - 1]) { temp = arr[i]; int low = 0; int high = i - 1; while (low <=high) { int mid = (low + high) / 2; if (arr[i] > arr[mid]) { low = mid + 1; } else if (arr[i] < arr[mid]) { high = mid - 1; } } for (j = i-1; j >= high+1 ; j--) { arr[j + 1] = arr[j]; } arr[high + 1] = temp; } } }
希尔排序(插入排序)
思想:
分为若干子表分别直接插入排序,最后当总体有序时进行一次直接插入排序。步长逐步减少。不能用于链表。
特点:
时间复杂度:O(N^1.3)(取决于选择的序列),时间复杂度:O(1)。不稳定,不能用于链表。
代码:
void ShellSort(int arr[], int len) { for (int gap = len / 2; gap >= 1; gap = gap / 2) //步长逐渐减小 { for (int i = gap; i < len; i++) //并不需要排完一个子序列再去排下一个子序列。 { if (arr[i] < arr[i - gap]) //无序,说明需要插入到前面的有序数组中 { int temp = arr[i]; int j; for (j = i - gap; j >= 0 && temp < arr[j]; j = j - gap)//找插入位置 arr[j + gap] = arr[j]; //移动元素。 arr[j + gap] = temp; } } } }
简单选择排序:
思想:
每次在待排序列表中选一个最小的排到前面去。
特点:
与初始序列无关,时间复杂度始终为O(N^2),空间复杂度为O(1)。不稳定.移动次数小,但是比较次数不变。
代码:
void selectsort(int arr[], int len) { for (int i = 0; i < len - 1; i++)//待排序的数组大小越来越小 { int min = i; //保存最小数的下标 for (int j = i + 1; j < len; j++) { if (arr[j] < arr[min]) min = j; } if (min != i) swap(arr[i], arr[min]); //交换数字 } }
堆排序 (选择排序)
思想:按照层次遍历的顺序标号映射到数组中。
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
建立初始堆,输出堆顶元素,将堆底元素送入堆顶,向下调整。
特点:
建堆时间为O(N),之后又n-1次向下调整,每次为O(logN),所以时间复杂度始终为O(NlogN)。不稳定。
删除堆顶元素:把它与堆的最后一个元素交换,向下调整。
插入操作:把新的节点放到堆的末尾,向上调整。
代码:
void adjust(int arr[], int len, int index) { int left = 2 * index + 1; // index的左子节点 int right = 2 * index + 2;// index的右子节点 int maxIdx = index; if (left<len && arr[left] > arr[maxIdx]) maxIdx = left; if (right<len && arr[right] > arr[maxIdx]) maxIdx = right; 找左右子节点最大的数交换 if (maxIdx != index) //如果将要发生交换 { swap(arr[maxIdx], arr[index]); adjust(arr, len, maxIdx); } } // 堆排序 void heapSort(int arr[], int size) { // 构建大根堆(从最后一个非叶子节点向上) for (int i = size / 2 - 1; i >= 0; i--) { adjust(arr, size, i); } // 调整大根堆 for (int i = size - 1; i >= 1; i--) { swap(arr[0], arr[i]); // 将当前最大的放置到数组末尾 adjust(arr, i, 0); // 将未完成排序的部分继续进行堆排序 } }
归并排序
特性
时间复杂度一直是O(nlogn),稳定,需要辅助数组
思想
采用分治的思想,将数组分为两段,分别归并排序,再合并。
代码
void Merge(int arr[], int l, int mid, int r) //合并两个数组 { int* help = new int[r - l + 1]; //辅助数组 int p1 = l; int p2 = mid + 1; int index = 0; while (p1 <= mid && p2 <= r) { help[index++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++]; } while (p1 <= mid) //复制剩余的元素 { help[index++] = arr[p1++]; } while (p2 <= r) { help[index++] = arr[p2++]; } for (int i = 0; i < r - l + 1; i++) //复制到原数组 { arr[l+i] = help[i]; } delete[] help; } void MergeSort(int arr[], int l, int r) { if (l == r) return; if (l < r) { int mid = (l + r) / 2; MergeSort(arr, l, mid); MergeSort(arr, mid+1, r); Merge(arr, l, mid, r); } }
基数排序
思想:
有LSD(最高位优先),MSD(最高位优先),先按某位数的不同分配到桶中,再收集起来,接着再按下一位分配和收集。
特点:
不依靠比较来排序,稳定。时间复杂度O(d(n+radix))。
来源:https://www.cnblogs.com/void-lambda/p/12379954.html