参考本人对于算法进行总结的代码仓:传送门
其中的sort
部分。
1.知识点
十大排序算法及其性质概览。
摘自:
Runoob十大排序算法
2. C++实现
2.1 冒泡排序
最基础相邻两数交换。
冒泡排序在完全逆序列的情况下的交换次数为n(n-1)/2
/** 1-冒泡排序 **/
template <typename T>
void bubbleSort(T nums[],int len){
for(int i=0;i<len-1;i++){
for(int j=0;j<len-1-i;j++){
if(nums[j]>nums[j+1]){
swap(nums[j],nums[j+1]);
}
}
}
}
template<typename T>
void bubbleSort(vector<T> &nums){
for(int i=0;i<nums.size()-1;i++){
for(int j=0;j<nums.size()-1-i;j++){
if(nums[j]>nums[j+1]){
swap(nums[j],nums[j+1]);
}
}
}
}
2.2 选择排序
从n个中选择最小的放置在最前面构成有序区,迭代…
/** 2-选择排序 **/
template <typename T>
void selectSort(T nums[],int len){
for(int i=0;i<len-1;i++){
int minInd = i;
for(int j=i+1;j<len;j++){
if(nums[j]<nums[minInd]){
minInd = j;
}
}
swap(nums[i],nums[minInd]);
}
}
2.3 插入排序
和扑克牌排序类似,从当前待排序位置往前搜索,该位比待插入数字大则往后移动,直到该位比待插入数字小,则将待插入数字插在该位后一位。
/** 3-插入排序 **/
/** 介绍:类似于扑克牌的排序,以第一个数字为有序区,后续数字为无序区;
* 从无序区取待插入数字temp,从当前位往左搜索,若该位大于temp,则将该位后移,直到找到第kk位小于temp,则将temp插入kk位的下一位
* 注意位的边界条件,一直循环以上过程,直到所有无序区均为变为有序 **/
/** 复杂度分析
* 时间复杂度:外循环n-1次,内循环平均为(1+n-1)/2次;因而总复杂度为O(n^2)
* 空间复杂度:只占用额外的temp,不随n变化,O(1)
* **/
template <typename T>
void insertionSort(T nums[],int len){
for(int i=1;i<len;i++){
int temp = nums[i]; // 取出当前待排序数字
int j=i-1;
while(j>=0 && nums[j]>temp){
nums[j+1]=nums[j]; // 该位大于temp,则右移
j--;
}
nums[j+1]=temp; //第j为小于temp,将temp插入至其下一位
}
}
template <typename T>
void insertSort(vector<T> &nums){
for(int i=1;i<nums.size();i++){
int key = nums[i];
int j = i-1;
while(j>=0 && nums[j]>key){
nums[j+1]=nums[j];
j--;
}
nums[j+1]=key;
}
}
2.4 希尔排序
过于庞杂,不掌握,略。
2.5 归并排序
归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用
分治法
(Divide and Conquer)的一个非常典型的应用。
有两种方式:
1)自上而下的递归(所有递归的方法都可以用迭代重写,所以就有了第 2 种方法);
2)自下而上的迭代;
归并排序的算法步骤如下:
1) 判断条件
2) 划分数组
3) 进行融合
融合函数merge(a,b)的步骤如下;
- 将b的数字融合进入a,则只有当
a中元素没用完
,并且b中元素用完
或者a中元素小于b中元素
时,将a中元素加入结果 - 否则将b中元素加入结果
vector<int> ans,v1,v2;
// 分支,中序遍历
midOrder(v1,root1);
midOrder(v2,root2);
int i = 0, j = 0;
while (i < v1.size() || j < v2.size()) {
if (i < v1.size() && (j == v2.size() || v1[i] <= v2[j])) {
ans.push_back(v1[i++]);
}
else {
ans.push_back(v2[j++]);
}
}
数组的归并排序
完整代码:
// 归并排序:mergeSort
template<typename T>
void merge(vector<T> &nums, int low, int mid, int high) {
int p1 = low, p2 = mid + 1;
vector<T> tmp;
while (p1 <= mid || p2 <= high) {
if (p1 <= mid && (p2 > high || nums[p1] < nums[p2])) {
tmp.push_back(nums[p1++]);
} else {
tmp.push_back(nums[p2++]);
}
}
// 归并
for (int lo = low,j=0; lo <= high; ++lo) {
nums[lo] = tmp[j++];
}
}
template<typename T>
void mergeSort(vector<T> &nums, int low, int high) {
if (low >= high) return;
int mid = low + (high - low) / 2;
mergeSort(nums, low, mid);
mergeSort(nums, mid + 1, high);
merge(nums, low, mid, high);
}
链表的归并排序
代码如下:
class Solution {
public:
ListNode* mergeTwoLists(ListNode* la,ListNode* lb){
if(la==NULL) return lb;
if(lb==NULL) return la;
if(la->val < lb->val){
la->next = mergeTwoLists(la->next,lb);
return la;
}
lb->next = mergeTwoLists(la,lb->next);
return lb;
}
// 归并排序
// 寻找链表中间节点,分治分别排序,归并
ListNode* sortList(ListNode* head) {
// 特判
if(!head || !head->next) return head;
// 快慢指针做分割
ListNode *slow = head,*fast = head->next;
while(fast!=NULL && fast->next!=NULL){
slow = slow->next;
fast = fast->next->next;
}
// 截断
ListNode *rhead = slow->next;
cout<<"rhead: "<<rhead->val<<endl;
slow->next = nullptr;
// 递归分治
return mergeTwoLists(sortList(head),sortList(rhead) );
}
};
2.5.1 合并二叉搜索树
2.6 快速排序
// 分区函数
template <typename T>
int partition(T A[], int low, int high) {
int pivot = A[low];
while (low < high) {
while (low < high && A[high] >= pivot) {
--high;
}
A[low] = A[high];
while (low < high && A[low] <= pivot) {
++low;
}
A[high] = A[low];
}
A[low] = pivot;
return low;
}
// 快排母函数
template <typename T>
void quickSort(T A[], int low, int high)
{
if (low < high) {
int pivot = partition(A, low, high);
quickSort(A, low, pivot - 1);
quickSort(A, pivot + 1, high);
}
}
2.7 堆排序
堆排序:堆方法
max_heapify
+堆排序母函数heapSort
代码如下:
/** 7-堆排序 **/
// 用於将[start,end]闭区间进行堆调整
void max_heapify(int arr[], int start, int end)
{
// 建立父節點指標和子節點指標
int dad = start;
int son = dad * 2 + 1;
while (son <= end)
{ // 若子節點指標在範圍內才做比較
if (son + 1 <= end && arr[son] < arr[son + 1]) // 先比較兩個子節點大小,選擇最大的
son++;
if (arr[dad] > arr[son]) // 如果父節點大於子節點代表調整完畢,直接跳出函數
return;
else
{ // 否則交換父子內容再繼續子節點和孫節點比較
swap(arr[dad], arr[son]);
dad = son;
son = dad * 2 + 1;
}
}
}
void heap_sort(int arr[], int len)
{
// 初始化,i從最後一個父節點開始調整
for (int i = len / 2 - 1; i >= 0; i--)
max_heapify(arr, i, len - 1);
// 先將第一個元素和已经排好的元素前一位做交換,再從新調整(刚调整的元素之前的元素),直到排序完畢
for (int i = len - 1; i > 0; i--)
{
swap(arr[0], arr[i]);
max_heapify(arr, 0, i - 1);
}
}
// 调整[start,end]该闭区间
template <typename T>
void max_heapify(vector<T> &nums,int start,int end)
{
if(start>=end) return;
int dad = start;
int son = 2*dad+1;
while(son<=end ){
// 选大的儿子节点
if(son+1<=end && nums[son+1]> nums[son]){
++son;
}
// 如果当前父节点大于儿子节点,则[start,end]该段线性表构成的小堆已经局部有序
if(nums[dad]>nums[son]){
return;
}else{
// 否则交换dad与son的值,并且下沉
swap(nums[dad],nums[son]);
dad = son;
son = 2*dad+1;
}
}
}
template <typename T>
void heapsort(vector<T> &nums)
{
// 初始化,建堆
int len = nums.size();
for (int i = len/2-1; i >=0 ; i--) {
max_heapify(nums,i,len-1); // 调整[i,len-1]该闭区间
}
// 调整,重复建堆排序,直到i=1
for(int i=len-1;i>0;i--){
swap(nums[0],nums[i]); // 交换大根堆堆顶至最后
max_heapify(nums,0,i-1); // 重新调整建堆
}
}