赞
踩
目录
学会使用堆排序首先了解堆的概念,堆是一个由数组储存的完全二叉树,它的逻辑结构是一颗二叉树,物理结构是一个数组。且任意子树的root的值是其左孩子和右孩子的值的最大值或最小值。堆分为大堆小堆,大堆:堆顶数据最大,小堆:堆顶数据最小。
小堆如下所例:
大堆如下:
由于堆是由数组顺序储存所以我们可轻松索引到某个节点。
堆中 父亲和儿子的下标关系为:
leftchild == parent*2 + 1 ; rightchild == parent*2 + 2 == leftchild + 1 ;
parent == ( child - 1 ) / 2 ;
第一步是建堆:
自底向上的方式建一个大堆(排升序)或建一个小堆(排降序),从最后一个非叶子节点(最后一个父亲节点)开始调整。
以建大堆为例:
实现原理:向下调整算法,在一任意一颗二叉树中若parent的值比child小,则将两个child中大的数值与parent互换,直到该二叉树中所有子树都满足大堆即可
- //交换两个节点的值
- void swap(int *p1, int*p2)
- {
- int tmp = *p1;
- *p1 = *p2;
- *p2 = tmp;
- }
- //向下调整算法 实现大堆
- void ADjustDown(int *arr, int n, int root)
- {
-
- int parent = root;
- int child = root * 2 + 1;
- while (child < n)
- {
- if (child+1< n && (arr[child] < arr[child + 1])) //(child+1)注意越界、选出两个child中较大的值
- {
- child += 1;
- }
- if (arr[child] > arr[parent])
- {
- swap(&arr[child], &arr[parent]);
- parent = child;
- child = parent * 2 + 1;
- }
- else
- {
- break;
- }
- }
-
- }
- //实现堆排序
- void HeapSort(int *arr, int n)
- {
- for (int i = (n - 1 - 1) / 2; i >= 0;--i) //自底向上 从最后一个parent节点开始调整,依次往上
- {
- ADjustDown(arr, n, i);
- }//循环走完 建堆完成
-
- //实现排序
- int end = n - 1;
- while (end > 0)
- {
- swap(&arr[end], &arr[0]); //将堆顶的数(该数组中最大的数)放到数组的组后一个位置
- ADjustDown(arr, end, 0); //继续向下调整,实现大堆
- end--;
- }
- //循环走完,排序完成
- }
建堆的时间复杂度O(n),排序选数的时间复杂度为O(logN),所有最终时间复杂度为N*logN。是一个非常高效的排序算法。
end先向左走,若找到比key小的值就往(坑)pivot里放,该数挪走了又出现一个新的坑,begin又向右走找到比key大的值就向坑里放,又会出现一个新的坑。循环下去,最终begin<=end时,将key值放到最后一个坑中去,最后实现key左边的都比key小,右边的都比key大,完成单趟排序。
- //单趟排序 挖坑法
- int PartSort1(int *arr,int left,int right)
- {
- int begin = left;
- int end = right;
- int index = GetMidindex(arr, left, right);
- swap(&arr[begin], &arr[index]);
- int key = arr[begin];
- int pivot = begin;
- //循环内实现左边小 右边大
- while (begin < end)
- {
- while (begin < end && arr[end] >= key)
- {
- --end;
- }
- arr[pivot] = arr[end];
- pivot = end;
- while (begin < end && arr[begin] <= key)
- {
- ++begin;
- }
- arr[pivot] = arr[begin];
- pivot = begin;
- }
- pivot = begin;
- arr[pivot] = key;
- return pivot;
- }
- //左右指针法 细节缺陷较多
- int PartSort2(int *arr, int left ,int right)
- {
- int begin = left;
- int end = right;
- int index = GetMidindex(arr, left, right);
- swap(&arr[left], &arr[index]);
- int keyi = begin;
- while (begin < end)
- {
-
- //end找小 while (begin<end && arr[end]>=arr[keyi])
- {
- --end;
- }
- //begin找大
- while (begin<end && arr[begin] <= arr[keyi])
- {
- ++begin;
- }
- swap(&arr[begin], &arr[end]); //互换begin和end的值
- }
- swap(&arr[begin], &arr[keyi]);
- return begin;
- }
最后cur=n-1时,再将prev和keyi互换,完成单趟排序
- //单趟排序 快慢指针法方便好用!
- int PartSort3(int*arr, int left, int right)
- {
- int prev = left;
- int cur = left + 1;
- int index = GetMidindex(arr, left, right);
- swap(&arr[left], &arr[index]);
- int keyi = left;
- while (cur <= right)
- {
- //升序cur找比arr[keyi]小交换,降序cur找大交换
- if (arr[cur] < arr[keyi])
- {
- ++prev;
- swap(&arr[prev], &arr[cur]); //通过prev使大的值往后扔
- }
- ++cur;
- }
- swap(&arr[prev], &arr[keyi]);
- return prev;
- }
最后实现整体排序,使用分治递归的思想,当左子区间有序,右子区间有序则该数组有序。将左右子区间继续分割,最终当区间中只有一个值时则该子区间有序。
- //递归实现
- void QuickSort(int*arr, int left,int right)
- {
- if (left >= right) //区间只有一个值时
- return;
- int indexkey = PartSort3(arr, left, right);
- //以下递归 为分治算法 满足key左区间有序 右区间有序,则该数组有序
- QuickSort(arr, left, indexkey - 1);
- QuickSort(arr, indexkey + 1, right);
- }
通过数据结构中的栈模拟操作系统中栈帧
- //非递归 实现快速排序
- void QuickSortNonR(int*arr, int n)
- {
- Stack st;
- StackInit(&st);
- StackPush(&st,n-1); //将数组的左右区间 压入栈中
- StackPush(&st, 0);
- while (!StackEmpty(&st))
- {
- int left=StackTop(&st);
- StackPop(&st);
- int right=StackTop(&st);
- StackPop(&st);
- int indexkey = PartSort2(arr, left, right);
- //如果被分子区间元素个数大于2 则继续入栈 出栈 操作
- //[left,indexkey-1] [indexkey+1,right]
- if (indexkey + 1 < right)
- {
- StackPush(&st, right);
- StackPush(&st, indexkey+1);
- }
- if (left < indexkey - 1)
- {
- StackPush(&st,indexkey-1);
- StackPush(&st, left);
- }
- }
- StackDestory(&st);
- }
代码实现
- //归并排序
- void _MergeSort(int*arr, int left, int right, int*tmp)
- {
- if (left >= right)
- {
- return;//该子区间只有一个数时
- }
- //分治 对半分解数组
- int mid = (left + right) >> 1;
- _MergeSort(arr, left, mid, tmp);
- _MergeSort(arr, mid + 1, right, tmp);
- //按升序归并
- int index = left;
- int begin1 = left, end1 = mid;
- int begin2 = mid + 1, end2 = right;
- while (begin1 <= end1 && begin2 <= end2)
- {
- if (arr[begin1] < arr[begin2]) //选大的向临时数组中拷贝
- {
- tmp[index++] = arr[begin1++];
- }
- else
- {
- tmp[index++] = arr[begin2++];
- }
- }
- while (begin1 <= end1)//当右区间归完,左区间还没归完时
- {
- tmp[index++] = arr[begin1++];
- }
- while (begin2 <= end2)//当左区间归完,右区间还没归完时
- {
- tmp[index++] = arr[begin2++];
- }
- //将临时数组的数据拷贝到原数组
- for (int i = 0; i <= right; i++)
- {
- arr[i] = tmp[i];
- }
- }
- void MergeSort(int*arr, int n)
- {
- int*tmp = (int*)malloc(sizeof(int)*n); //创建一个临时数组用来存放排好序的数
- _MergeSort(arr,0, n-1, tmp);
- free(tmp);
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。