基本思想,实现框架,必要时画流程图
希尔排序
定义一个间隔,初始一般为数组的长度一半,将整个数组的元素按间隔归为多个组,使组内元素有序;再逐渐缩小间隔,一般在原来基础上再除以一半,重复上述过程,直到间隔缩为1,即数组本身,进行一次插入排序。而此时由于各个元素已经基本有序,所以对整个数组的排序只需移动很少的元素。
for(int gap = n/2; gap >=1; gap /= 2){
for(int i = 0; i < gap; i++){
for(int j = i+gap; j < n; j+=gap){
int temp = v[j];//插入排序,记录待插入结点
int k = j - gap;//同一组的前一个元素,需要向前遍历到插入位置
if(temp > v[k]){//降序,大数在前
while(k >= 0 && temp > v[k]){//循环找到插入位置,k不能越界
v[k + gap] = v[k];//将元素后移
k -= gap;
}
v[k + gap] = temp;
}
}
}
}
要点:
i用来选取不同组,j用来选取组内的下一个元素,j 每次增加gap,在i确定的组中遍历组内所有元素
gap循环到1结束
快速排序
采用分治法,整个数组排序的实现建立在递归子区间均已排序构成的合集。
每轮递归确定一个基准元素的位置,再以基准元素为中心将数组分为基准元素左右两个区间继续递归。
设置一个基准元素pivot(一般选首个元素或末尾元素),和两个分别指向区间的首部指针low和尾部指针high,!当选取首个元素为pivot时,先移动右指针high,右指针向左移动直到遇到比基准元素pivot小的元素然后停下,再向右移动左指针low直到遇到第一个比pivot大的元素上,交换左右指针对应的元素。重复直到左指针等于右指针,将pivot与low对应的元素交换,此时在整个数组中pivot基准元素就确定好了自己的最终位置。以基准元素二分区间继续递归
int oneSort(vector<Book> &v, int low, int high){
int pivot = low;//pivot等于区间最左点low
while(low < high){
while(low < high && v[high].monthTicket >= v[pivot].monthTicket)//pivot为首元素,则high指针先移动
high--;
while (low < high && v[low].monthTicket <= v[pivot].monthTicket)//枢纽选的第一个数,low从0开始,包含=
low++;
if(low < high)
swap(v[high], v[low]);
}
swap(v[low], v[pivot]);
return low;//low是枢纽确定后的位置
}
void quickSort(vector<Book> &v){
stack<pair<int, int>> s;
s.push({0, v.size()-1});
while(!s.empty()){
弹出左右指针
int mid = oneSort(v, low, high);//oneSort返回pivot的下标
if(mid-1 > low)
左右指针入栈
if(mid+1 < high)
左右指针入栈
}
}
要点:
由于数据量大,不采用递归实现,而使用栈数据结构模拟递归
左右指针那个先移动与pivot选取有关,pivot为首元素,则右指针先移动
左右指针相等时,即为pivot元素的最终位置
非递归方法将区间的左右端点,即左右指针作为pair压入栈中,注意左右指针不能越界
堆排序
将数组构建出完全二叉树,从最后一个内结点向上调整,若为大根堆,则从最后一个内结点往上,每次将此根节点与左右子节点中的较大者作为根节点,直到最后完全二叉树的根节点就确定为最大数,记录到数组中。再将数组中最后一个元素覆盖到根节点位置,再次从最后一个内结点开始向上调整堆,直到堆中所有元素均已被记录
int heapSort(vector<Book> v){
vector<int> ret;
for(int n =v.size(); n > 0; n--){
for(int i = (n-1-1)/2; i>=0; i--){
找到左右子节点的最大者
if(最大者大于根节点){
交换最大者与根节点
}
}
ret.push_back(v[0]);//记录最大元素到返回数组
v[0] = v[n-1];//用尾元素覆盖根节点
}
return ret;
}
要点:
n-1为尾元素,(index - 1)/2为index的父节点,父节点*2为左孩子,*2+1为右孩子
从最后一个内结点(尾结点的父节点开始向上调整)
基数排序
待比较数字的拥有不同的位权,在相同的位权上可以进行排序,再逐渐升高位权,例如10, 22, 7,3, 54,按照个位上排序后为10, 22, 3, 54, 7,在此排序结果上再按照十位排序,结果为3, 7, 10, 22, 54,此时数组已有序。
vector<int> baseSort(vector<int> &v){
vector<vector<int>> ctnr(10);//十位数每位范围为0-9
int n, maxindex, maxnum = 0;//n记录最大数的位数
for(int i =0; i < v.size(); i++){
找到位数最长的数字
}
for(int f = 10, temp = v[maxindex]; temp > 0; n++){//确定最大数的位数,用n记录
temp /= f;
}
for(int f = 1, times = 0; times < n; f *= 10, times++){
for(int i =0; i < v.size(); i++){
ctnr[v[i] /f %10].push_back(v[i]);//将数组中的数字按照位值,类似桶排序放入临时数组的对应下标,如个位模的结果为3,则放入临时数组3下标的位置
}
int cnt = 0;
for(auto i = ctnr.begin(); i != ctnr.end(); i++){
for(auto j = i->begin(); j != i->end(); ){
v[cnt++] = *j;//将新排序的数组覆盖原数组
j = i->erase(j);
}
}
}
return v;
}
要点:
先要确定最大数有多少位
按位排序记录的数组大小为10
每次新的排序都以上次已经按位排好的结果为基础
归并排序
将多个有序的子区间合并成有序的整个数组,初始将数组分成多个长度为2的子区间,子区间内排序;逐渐增大子区间的长度并排序,直到子区间的长度为整个数组的长度。
void merge(vector<int> &v, int low, int middle, int high){
int i = low, j = middle+1, k =0;
vector<int> temp;
while (i <= middle && j <= high){
将i与j对应元素中的较小者放入temp数组
}
while (i <= middle)//将middle左区间剩余元素加入temp
temp.push_back(v[i++]);
while (j <= high)//将middle右区间剩余元素加入temp
temp.push_back(v[j++]);
// 将排好序的存回v中low到high该区间内
for (i = low, k = 0; i <= high; i++, k++)
v[i] = temp[k];
}
vector<int> mergeSort(vector<int> &v){
int n = v.size();
int size = 1, low, middle, high;//size为区间长度
while (size <= n){
low = 0;//low每轮循环初值为0
while (low + size <= n){
middle = low + size - 1;
high = middle + size;
if(high >= n)//注意单独元素越界问题!!!
high = n-1;
merge(v,low,middle,high);
low = high + 1;
}
size *= 2;
}
}
要点:
middle = low + size - 1,high = middle + size;即middle可与low重合
high不能越界
下一个区间从上一个区间的右边界high的下一个位置开始