算法专辑1:排序

参考本人对于算法进行总结的代码仓:传送门
其中的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); // 重新调整建堆
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值