排序
第一次写博客咯!容我先絮絮叨叨一会儿哈哈哈。
以前总是上论坛看别人的代码,也写、改过一些代码,但都瞎存在硬盘里没有整理归纳,即使代码跑通了、自己思考过也印象不深。写博客做记录、复述编程思想有利于加深理解,也可以和广大优秀的伙伴们讨论我理解中的不足。
俺在武汉家中从1月份待到8月了,导师学校不管不问,不仅受歧视还纯放养,躁郁症都快犯了。之前买了一套树莓派小车零件,拼好了能实现网页控制小车运动、接受车载摄像头传入的视频等基本功能,晚一点把这块儿资料也整理到博客上来和大家分享。自觉代码能力实在太差,提升空间巨大,于是最近买了几本关于数据结构和简单算法的书,一边刷刷简单leetcode习题一边把数据结构学习一下,以提升能力。虽然本科大一上过c++的课有一丢丢基础,但计算机思维和人类思维相差甚远,那时候刚接触编程畏难情绪占了上风,所以心理上对写代码有点抵触,每次小作业都能让我像掉了层皮。。。但是现在发现代码能力很重要啊,写程序的人相当于新时代的翻译,人类把想要实现的功能翻译成计算机语言然后让电脑代替人类去干活,这种“翻译”无论在工作还是科研中都必不可少。
言归正传,本文放上堆排序和快速排序的代码。
堆排序
从数据的存储结构看,最大堆/最小堆是一个数组。
从数据的逻辑结构看,最大堆/最小堆是一棵完全二叉树.
参考资料:
B站近视机器人排序的视频.很有意思~这里siftdown\heapify含义同
另一博士小哥讲解视频.很棒,思想和代码借鉴于此。我照着这里代码写的c++,函数名不一样,部分讲解的话只看视频可能理解上有点模糊,所以我多写了点注释。
heap属性:父节点大于子节点,若完全二叉树满足此性质则为heap
【siftdown】
**功能:**实现以该节点为父的所有子树均成堆。
**实现方法:**将父节点与较大子节点交换(与下层元素交换),对交换后打破的原平衡(该子节点失衡,但该子节点以下依然保持原序)递归siftdown调整。
提供2种方法,分别为递归和非递归(循环法)。
**使用前提:**二叉树需要满足较严格的条件,该父节点与其子节点可能不满足heap性质,但其子节点以下均满足heap性质。
【heapify】
**功能:**乱序二叉树成堆,拥有heap属性。
**实现方法:**从最后一个非叶子节点起,按层序反向遍历siftdown,遍历过的节点以下全部出成堆。
【heapSort】
**功能:**堆排序。实现方法:首先heapify将乱序数组成堆,堆顶为数组最大值,将其拎出,与数组末端元素交换,最大值置于数组末端。此时原末端数据置于堆顶,堆顶父节点失衡,进行siftdown,使长度为length-1的数组重新成堆。以此类推。
【siftup】
功能:一个个往堆中插入元素;若大于父节点,则与上层元素交换。这里暂时没用到。
heapsort.cpp
#include<iostream>
#include<vector>
using namespace std;
void mySwap(vector<int>& nums,int index1,int index2)
{//交换 加引用该变量可修改
int temp=nums[index1];
nums[index1]=nums[index2];
nums[index2]=temp;
}
void printarray(vector<int>& nums)
{
for(int i=0;i<nums.size();i++)
{
cout<<nums[i]<<" ";
}
cout<<endl;
}
//
void siftDown(vector<int>& nums,int parent,int length)
{//较大值交换至父节点。对一个节点做siftDown的时,必须保证它的所有子树都已经是堆。
if(parent>=length-1)
{
return;
}
int child1=2*parent+1;
int child2=2*parent+2;
int max=parent;
if(nums[child1]>nums[max]&&child1<length)
{
max=child1;
}//先判断左孩子
if(nums[child2]>nums[max]&&child2<length)
{
max=child2;
}
if(max!=parent)
{
mySwap(nums,max,parent);
siftDown(nums,max,length);//对交换前标记为max的节点继续。保证每次交换根和子节点数据以后,以子节点为根的那棵树也符合条件
}
}
void heapify(vector<int>& nums,int length)
{//从最后一个父节点开始下沉
int lastChild=length-1;
int lastParent=(lastChild-1)/2;
for(int i = lastParent;i>=0;i--)
{
siftDown(nums,i,length);
}
return;
}
void heapSort(vector<int>& nums,int length)
{//首先heapify将乱序数组成堆,堆顶为数组最大值,将其拎出,与数组末端元素交换,最大值置于数组末端。此时原末端数据置于堆顶,堆顶父节点失衡,进行siftdown,使长度为length-1的数组重新成堆。以此类推。
heapify(nums,length);
for(int i=length-1;i>=0;i--)
{
mySwap(nums,i,0);
siftDown(nums,0,i);//每轮数组长度为i
}
}
int main()
{
vector<int> arr={2,5,7,3,1,10,4,6,9,8};
printarray(arr);
// siftDown(arr,0,arr.size());
// printarray(arr);
// heapify(arr,arr.size());
// printarray(arr);
heapSort(arr,arr.size());
printarray(arr);
return 0;
}
siftdown不用递归的方法:
heapsort2.cpp
void siftDown(vector<int>& nums,int parent,int length)
{//较大值交换至父节点。对一个节点做siftDown的时,必须保证它的所有子树都已经是堆
int child1=2*parent+1;
int child2=2*parent+2;
int max;
while(child1<length)//循环
{
max=parent;
if(nums[child1]>nums[max])
{
max=child1;
}
if(child2<length&&nums[child2]>nums[max])//记得防止越界啊!加上判断条件!
{
max=child2;
}
if(parent!=max)
{
mySwap(nums,max,parent);
}
else
return;
parent=max;
child1=2*parent+1;
}
}
快速排序
very强大的排序算法,B站也有魔性视频hhh
机器人.
魔性排序舞蹈.
各种排序可视化比较.
双边循环
分治:
以第一个元素为pivot,两个指针分别从数组前端、末端向中间遍历。当左指针left所指数据>pivot以及right所指<pivot时,交换两个数据,继续遍历当两指针重合时,与pivot交换,此时满足pivot左边的数都比pivot小,右边的数都比pivot大。
自己写的,分治算法先搜索左边
quicksort2.cpp
因为若重合指针所指的数>pivot,再交换所得序列就有问题,所以额外加了两个判断,以使left-1所指与pivot交换。。。
#include<iostream>
#include<vector>
using namespace std;
void mySwap(vector<int>& nums,int index1,int index2)
{//交换 加引用该变量可修改
int temp=nums[index1];
nums[index1]=nums[index2];
nums[index2]=temp;
}
void printarray(vector<int>& nums)
{
for(int i=0;i<nums.size();i++)
{
cout<<nums[i]<<" ";
}
cout<<endl;
}
int myPartition(vector<int>& arr,int startIndex, int endIndex)
{//分治双边循环 自己写
int pivot=arr[startIndex];
int pivotIndex=startIndex;
int left=startIndex,right=endIndex;
while(left<right)
{
if(arr[left]<=pivot)
{
left++;
continue;
}
if(arr[right]>=pivot)
{
right--;
continue;
}
mySwap(arr,left,right);
left++;
right--;
}
if(arr[left]<=pivot)
{
pivotIndex=left;
}
else
{
pivotIndex=left-1;
}
pivotIndex=right;
mySwap(arr,startIndex,pivotIndex);
return pivotIndex;
}
void quickSort(vector<int>& arr,int startIndex,int endIndex)//递归函数
{//快速排序
if(startIndex>=endIndex)
{
return;
}
int pivotIndex=myPartition(arr,startIndex,endIndex);
quickSort(arr,startIndex,pivotIndex-1);
quickSort(arr,pivotIndex+1,endIndex);
}
int main()
{
vector<int> arr={4,7,3,5,6,2,8,1,0};
printarray(arr);
//cout<<"6 is in pos "<<myPartition(arr,0, arr.size()-1)<<endl<<endl;
quickSort(arr,0,arr.size()-1);
printarray(arr);
return 0;
}
常规算法,分治算法先搜索右边
参考算法之旅,自己改的
int myPartition(vector<int>& arr,int startIndex, int endIndex)
{//分治双边循环 自己写
int pivot=arr[startIndex];
int pivotIndex=startIndex;
int left=startIndex,right=endIndex;
while(left<right)
{
while(arr[right]>=pivot)//从右边开始搜索
{
right--;
}
while(arr[left]<=pivot)
{
left++;
}
if(left<right)
{
mySwap(arr,left,right);
right--;
left++;
}
// mySwap(arr,left,right);//重合时交换无意义
}
pivotIndex=right;
mySwap(arr,startIndex,pivotIndex);
return pivotIndex;
}
大话数据结构方法
partition_two_points.cpp
int myPartition(vector<int>& arr,int startIndex, int endIndex)
{//分治双边循环 大话数据结构
int pivot=arr[startIndex];
int left=startIndex,right=endIndex;
while(left<right)
{
while(left<right&&arr[right]>=pivot)
{
right--;
}
mySwap(arr,left,right);//比pivot小的交换到左边
while(left<right&&arr[left]<=pivot)
{
left++;
}
mySwap(arr,left,right);//比pivot大的交换到右边
}
return left;
}
单边循环(小灰的算法之旅)
quicksort.cpp
int myPartition(vector<int>& arr,int startIndex, int endIndex)
{//分治单边循环 小灰的算法之旅
int pivot=arr[startIndex];
int pivotIndex=startIndex;
for(int i=startIndex+1;i<=endIndex;i++)
{
if(arr[i]<pivot)
{
pivotIndex++;
mySwap(arr,i,pivotIndex);
}
}
arr[startIndex]=arr[pivotIndex];
arr[pivotIndex]=pivot;
return pivotIndex;
}