排序 算法

news/2024/9/28 13:21:50/

八大算法>排序算法 | 爱编程的大丙 (subingwen.cn)

  •  冒泡排序

best: O(n), worst O(n^2)
* 数组的相邻两个元素进行比较:nums[i] > nums[i+1] -> swap(nums[i], nums[i+1])
* 一次遍历后,最大元素移至数组末尾

* n长度数组,进行n-1次排序,每次排序遍历的数组长度-1

  • 选择排序

  * best: O(n^2), worst: O(n^2)
    * -将数组分为 已排序部分 与 未排序部分(初始为空)
    * -遍历未排序部分,找到当前的max元素坐标 --> max_index
    * -将nums[max_index]与未排序部分的第一个元素nums[head_index]交换 --> swap(nums[max_index], nums[head_index])
    * -n长度数组,进行n-1次排序,每次排序遍历后,未排序数组的长度-1,即遍历起始点+1

  • 插入排序

   * best: O(n), worst:O(n^2)
    * -将数组分为 已排序部分 与 未排序部分(初始为数组头元素)
    * -每次排序,取未排序部分的头元素tmp=nums[i] 从已排序部分的末尾nums[j]=nums[i-1]开始遍历比较
    *    if tmp < nums[j], nums[j+1]=num[j] 
    *    else if tmp <= nums[j], nums[j+1]=tmp
    * n长度的数组,要n次排序,每次排序后,未排序数组长度-1,遍历起始点+1

  • 希尔排序(优化-分组插入排序)

* best: O(n), worst:O(n^2)
    * -step1.将原数组分为间隔为gap的数组
    *    eg:  [1, 2, 3, 4, 5]  --> [1*, 2, 3* ,4, 5*]   原数组以gap=2,分为两个交叉数组
    * -step2.各分组数组内进行插入排序(在已排序部分比较时,以gap移位)
    * -step3.逐步缩小gap至1,进行一次gap=1的插入排序

  • 快排(递归分支-二叉树前序递归)

*best:O(nlogn) , worst:O(n^2) 

    * 分治思想:大问题拆分为小问题
    * -在数组中随机选择一个数作为当前数组的基准mid(eg:mid=nums[left])
    * -通过双指针(left=nums_begin,right=nums_end):将数组分为[小于基准部分, mid, 大于基准部分]
    * -递归:继续分治[小于基准部分],[大于基准部分] (until 分支数组元素为1)

  • 归并排序(递归分支-二叉树后序递归)

* best: O(nlogn), worst: O(nlogn)
    * -先将数组按中间点mid=(left+right)>>1拆分为两个子数组
    * -压栈:递归重复上一步,直至子数组元素为1
    * -出栈:在当前递归层,有序合并两个有序子数组[left, mid-1]与[mid, right]
    * -回到初始层级时,整个数组即有序

class Solution {
public:/* 1. 冒泡   ** best: O(n), worst O(n^2)* -数组的相邻两个元素进行比较:nums[i] > nums[i+1] -> swap(nums[i], nums[i+1])* -一次遍历后,最大元素移至数组末尾* -n长度数组,进行n-1次排序,每次排序遍历的数组长度-1*/void pubble_sort(vector<int>& nums){// 排序次数for(int i=0; i<nums.size()-1; i++){int swap_flag = false;// 未排序部分逐个比较->swapfor(int j=0; j<nums.size()-i-1; j++){if( nums[j] > nums[j+1] ){swap(nums[j], nums[j+1]);swap_flag = true;}}if(!swap_flag){break; }}}/* 2.选择  ** best: O(n^2), worst: O(n^2)* -将数组分为 已排序部分 与 未排序部分(初始为空)* -遍历未排序部分,找到当前的max元素坐标 --> max_index* -将nums[max_index]与未排序部分的第一个元素nums[head_index]交换 --> swap(nums[max_index], nums[head_index])* -n长度数组,进行n-1次排序,每次排序遍历后,未排序数组的长度-1,即遍历起始点+1*/void select_sort(vector<int>& nums){int tmp_index;// 排序次数for(int i=0; i<nums.size()-1; i++){tmp_index = i;// 未排序部分比较->find min/max -> 与未排序部分的1st元素swapfor(int j=i+1; j<nums.size(); j++){tmp_index = nums[tmp_index] > nums[j] ? j : tmp_index;}if(tmp_index != i)swap(nums[tmp_index], nums[i]);}}/* 3. 插入排序-1 ** best: O(n), worst:O(n^2)* -将数组分为 已排序部分 与 未排序部分(初始为数组头元素)* -每次排序,取未排序部分的头元素tmp=nums[i] 从已排序部分的末尾nums[j]=nums[i-1]开始遍历比较*    if tmp < nums[j], nums[j+1]=num[j] *    else if tmp <= nums[j], nums[j+1]=tmp* n长度的数组,要n次排序,每次排序后,未排序数组长度-1,遍历起始点+1*/void insert_sort(vector<int>& nums){// 未排序部分 gap=1for(int i=1; i<nums.size(); i++){int tmp = nums[i];int j = i - 1;// 已排序部分while(j>=0 && nums[j] > tmp){nums[j+1] = nums[j];j--;}nums[j+1] = tmp;}}/*4. 希尔排序-优化的插入排序  ** best: O(n), worst:O(n^2)* -step1.将原数组分为间隔为gap的数组*    eg:  [1, 2, 3, 4, 5]  --> [1*, 2, 3* ,4, 5*]   原数组以gap=2,分为两个交叉数组* -step2.各分组数组内进行插入排序(在已排序部分比较时,以gap移位)* -step3.逐步缩小gap至1,进行一次gap=1的插入排序*/void shell_sort(vector<int>& nums){// 以gap分组for(int gap=nums.size()/2; gap>0; gap/=2){// 未排序部分for(int i=gap; i<nums.size(); i++){int tmp = nums[i];int j=i-gap;// 已排序部分while(j>=0 && nums[j]>tmp){nums[j+gap] = nums[j];j -= gap;}nums[j+gap] = tmp;}}}/*5. 快排(递归分治-二叉树前序递归) ** best:O(nlogn) , worst:O(n^2)* 分治思想:大问题拆分为小问题* -在数组中随机选择一个数作为当前数组的基准mid(eg:mid=nums[left])* -通过双指针(left=nums_begin,right=nums_end):将数组分为[小于基准部分, mid, 大于基准部分]* -递归:继续分治[小于基准部分],[大于基准部分] (until 分支数组元素为1)*/void quick_sort(vector<int>& nums, int left, int right){//if(left >= right) return;//int mid = nums[left];int begin=left, end=right;while(left < right){while(left < right && nums[right] >= mid) right--;if(left!=right) nums[left] = nums[right];while(left < right && nums[left] <= mid) left++;if(left!=right) nums[right] = nums[left];}nums[left] = mid;quick_sort(nums, begin, left-1);quick_sort(nums, right+1, end);}/* 6.归并排序 (递归分支-二叉树后序递归)* best: O(nlogn), worst: O(nlogn)* -先将数组按中间点mid=(left+right)>>1拆分为两个子数组* -压栈:递归重复上一步,直至子数组元素为1* -出栈:在当前递归层,有序合并两个有序子数组[left, mid-1]与[mid, right]* -回到初始层级时,整个数组即有序*/void merge_sort(vector<int>& nums, int left, int right){if(left >= right) return ;// 递归分组int mid = (right + left) >> 1;merge_sort(nums, left, mid);merge_sort(nums, mid+1, right);// 有序合并当前层的2个有序数组[left, mid-1]与[mid, right]int l = left, r = mid+1;vector<int> tmp;while(l<=mid && r<=right){if(nums[l] < nums[r]) tmp.push_back(nums[l++]);else tmp.push_back(nums[r++]);}while(l <= mid) tmp.push_back(nums[l++]);while(r <= right) tmp.push_back(nums[r++]);for(int i=left, k=0; i<=right; i++,k++){nums[i] = tmp[k];}}vector<int> sortArray(vector<int>& nums) {quick_sort(nums, 0, nums.size()-1);return nums;}
};

(归并排序)以O(nlogn)时间复杂度解决链表排序问题

148. 排序链表 - 力扣(LeetCode)

step1.快慢指针找到链表的中间节点

step2.归并排序:有序合并两个链表数组

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
private:// 合并两个链表ListNode* merge(ListNode* list1, ListNode* list2){ListNode* dummyHead = new ListNode(0);ListNode* cur = dummyHead;while(list1 && list2){if(list1->val < list2->val){cur->next = list1;list1 = list1->next;}else{cur->next = list2;list2 = list2->next;}cur = cur->next;}cur->next = list1 ? list1 : list2;return dummyHead->next;}// 归并排序ListNode* merge_sort(ListNode* head, ListNode* end){// 叶节点if(head == nullptr) return head;if(head->next == end){head->next = nullptr;return head;}// find 链表中间结点ListNode* fast = head, *slow = head;while(fast != end){fast = fast->next;slow = slow->next;if(fast != end) fast = fast->next;}ListNode* mid = slow;// 分组// ListNode* list_left = merge_sort(head, mid);// ListNode* list_right = merge_sort(mid, end);// 有序mergereturn merge(merge_sort(head, mid),  merge_sort(mid, end));}
public:ListNode* sortList(ListNode* head) {return merge_sort(head, nullptr);}
};


http://www.ppmy.cn/news/1531530.html

相关文章

怎么通过AI大模型开发一个网站?

目录 一、提示词与AI输出 二、网站效果 以前不会代码开发&#xff0c;写网站是不可能的事情&#xff0c;现在有了AI&#xff0c;一切都有了可能。以下是我通过通义千问大模型开发的简单网站。 一、提示词与AI输出 提示词1 你是python程序员&#xff0c;我有一个大的需求&am…

教师课堂管理测评:构建高效与和谐的教学环境

在教育的广阔天地里&#xff0c;课堂是知识传递、思维碰撞与情感交流的核心舞台。良好的课堂管理不仅是教学质量的基石&#xff0c;更是促进学生全面发展、培养未来社会所需人才的关键。本文将从课堂规则、课堂环境、时间管理、学生参与、行为管理、沟通技巧、资源管理、学生差…

改变安全策略的五大实践

随着网络威胁形势的加剧&#xff0c;网络安全计划必须不断发展以保护组织的使命。 为了管理这种持续的网络安全发展&#xff0c;应遵循五项关键的安全计划变更管理实践&#xff1a; 1. 识别并吸引受安全风险影响的业务利益相关者 随着新的网络安全风险被发现&#xff0c;受影…

K8s容器运行时,移除Dockershim后存在哪些疑惑?

K8s容器运行时&#xff0c;移除Dockershim后存在哪些疑惑&#xff1f; 大家好&#xff0c;我是秋意零。 K8s版本截止目前&#xff08;24/09&#xff09;已经发布到了1.31.x版本。早在K8s版本从1.24.x起&#xff08;22/05&#xff09;&#xff0c;默认的容器运行时就不再是Doc…

综合实践:JPA+Thymeleaf 增删改查

在Java Web开发中&#xff0c;使用JPA&#xff08;Java Persistence API&#xff09;作为ORM&#xff08;对象关系映射&#xff09;框架和Thymeleaf作为模板引擎来实现增删改查&#xff08;CRUD&#xff09;操作是一种常见且高效的方式。以下是一个简单的示例&#xff0c;展示如…

容器编排工具Docker Compose

目录 一、Docker Compose概述 1、主要功能 2、工作原理 二、常用命令参数 1、服务管理 2、构建和重新构建服务 三、Docker Compose的yml文件 1、服务 2、网络 3、存储卷 四、容器编排实现haproxy和nginx负载均衡 一、Docker Compose概述 1、主要功能 定义服务&#xf…

OpenCV 进行图像分割

介绍 图像分割是将数字图像划分互不相交的区域的过程,它可以降低图像的复杂性,从而使分析图像变得更简单。 图像分割技术 阈值法 基于边缘的分割 基于区域的分割 基于聚类的分割 基于分水岭的方法 基于人工神经网络的分割 在这里,我们选择基于聚类的分割 与分类算法不同,…

Python 多进程解析:Multiprocessing 高效并行处理的奥秘

Python 多进程解析&#xff1a;Multiprocessing 高效并行处理的奥秘 文章目录 Python 多进程解析&#xff1a;Multiprocessing 高效并行处理的奥秘一 多进程1 导入进程标准模块2 定义调用函数3 创建和启动进程 二 存储进程结果 Queue三 threading & multiprocessing 对比1 …