数据结构学习笔记5-寻找最小的k个数(选择排序和堆排序)
来源:互联网 发布:淘宝秒杀不能付款 编辑:程序博客网 时间:2024/05/22 15:33
本文摘自《寻找最小的k个数》
题目描述
输入n个整数,输出其中最小的k个。
分析与解法
解法一
要求一个序列中最小的k个数,按照惯有的思维方式,则是先对这个序列从小到大排序,然后输出前面的最小的k个数。
至于选取什么的排序方法,我想你可能会第一时间想到快速排序(我们知道,快速排序平均所费时间为n*logn),然后再遍历序列中前k个元素输出即可。因此,总的时间复杂度:O(n * log n)+O(k)=O(n * log n)。
解法二
咱们再进一步想想,题目没有要求最小的k个数有序,也没要求最后n-k个数有序。既然如此,就没有必要对所有元素进行排序。这时,咱们想到了用选择或交换排序,即:
1、遍历n个数,把最先遍历到的k个数存入到大小为k的数组中,假设它们即是最小的k个数;
2、对这k个数,利用选择或交换排序找到这k个元素中的最大值kmax(找最大值需要遍历这k个数,时间复杂度为O(k));
3、继续遍历剩余n-k个数。假设每一次遍历到的新的元素的值为x,把x与kmax比较:如果x < kmax ,用x替换kmax,并回到第二步重新找出k个元素的数组中最大元素kmax‘;如果x >= kmax,则继续遍历不更新数组。
每次遍历,更新或不更新数组的所用的时间为O(k)或O(0)。故整趟下来,时间复杂度为n*O(k)=O(n*k)。
经典排序算法 - 选择排序Selection sort
顾名思意,就是直接从待排序数组里选择一个最小(或最大)的数字,每次都拿一个最小数字出来,
顺序放入新数组,直到全部拿完
再简单点,对着一群数组说,你们谁最小出列,站到最后边
然后继续对剩余的无序数组说,你们谁最小出列,站到最后边
再继续刚才的操作,一直到最后一个,继续站到最后边,现在数组有序了,从小到大
举例
先说看每步的状态变化,后边介绍细节,现有无序数组[6 2 4 1 5 9]
第一趟找到最小数1,放到最前边(与首位数字交换)
交换前:| 6 | 2 | 4 | 1 | 5 | 9 |
交换后:| 1 | 2 | 4 | 6 | 5 | 9 |
第二趟找到余下数字[2 4 6 5 9]里的最小数2,与当前数组的首位数字进行交换,实际没有交换,本来就在首位
交换前:| 1 | 2 | 4 | 6 | 5 | 9 |
交换后:| 1 | 2 | 4 | 6 | 5 | 9 |
第三趟继续找到剩余[4 6 5 9]数字里的最小数4,实际没有交换,4待首位置无须交换
第四趟从剩余的[6 5 9]里找到最小数5,与首位数字6交换位置
交换前:| 1 | 2 | 4 | 6 | 5 | 9 |
交换后:| 1 | 2 | 4 | 5 | 6 | 9 |
第五趟从剩余的[6 9]里找到最小数6,发现它待在正确的位置,没有交换
排序完毕输出正确结果[1 2 4 5 6 9]
第一趟找到最小数1的细节
当前数组是| 6 | 2 | 4 | 1 | 5 | 9 |
先把6取出来,让它扮演最小数
当前最小数6与其它数一一进行比较,发现更小数就交换角色
当前最小数6与2比较,发现更小数,交换角色,此时最小数是2,接下来2与剩余数字比较
当前最小数2与4比较,不动
当前最小数2与1比较,发现更小数,交换角色,此时最小数是1,接下来1与剩余数字比较
当前最小数1与5比较,不动
当前最小数1与9比较,不动,到达末尾
当前最小数1与当前首位数字进行位置交换,如下所示
交换前:| 6 | 2 | 4 | 1 | 5 | 9 |
交换后:| 1 | 2 | 4 | 6 | 5 | 9 |
完成一趟排序,其余步骤类似
我的代码如下:
#include<stdio.h>#include<string.h>#include<stdlib.h>void swap(int *l,int i,int j){ int temp=0; temp=*(l+i); *(l+i)=*(l+j); *(l+j)=temp;}int selectSort(int *l,int length)//找出假设的l的最大值{ int i,j,max;// for(i=0;i<length;i++) { i=0; max=i;//假定第i个是最大值,赋给max,注意,是下标 for(j=i+1;j<length;j++)//j从i+1开始往后轮询 { if(*(l+max)<*(l+j))//如果找到更大的,则交换下标 max=j; } //if(i!=max)//如果两者不等,说明需要此时的max下标对应的值才是真正的第i个大的值 // swap(l,i,max);//交换两者的值 } return max;}void findKSmallest(int *num,int numsize, int k){ int max=0; if(num==NULL)//如果输入的num数组是空,则异常退出 exit(-1); max=selectSort(num,k);//找出假设的p的最大值 for(int jj=k;jj<numsize;jj++) { if(*(num+jj)<*(num+max))//如果jj对应的值比p中最大值还大,那么需要交换 { swap(num,jj,max); max=selectSort(num,k); } } for(int j=0;j<k;j++) { printf("the %d is %d\n",j,*(num+j)); }}int main(){ int num[]={1,3,5,78,9,6}; findKSmallest(num,sizeof(num)/sizeof(int),5); return 0;}
解法三:利用堆排序:
更好的办法是维护容量为k的最大堆,原理跟解法二的方法相似:
1、用容量为k的最大堆存储最先遍历到的k个数,同样假设它们即是最小的k个数;
2、堆中元素是有序的,令k1
#include<stdio.h>void swap(int *l,int i,int j)//交换两数{ int temp=0; temp=*(l+i); *(l+i)=*(l+j); *(l+j)=temp;}void MaxHeapify(int *a,int length,int i)//从i开始往后排列成最大堆{ int left=i*2;//左孩子下标,利用完全二叉树的性质 int right=i*2+1;//右孩子下标 int largest=0;//初始化最大的值的下标 if(left<length && a[left]>a[largest]) largest=left; else largest=i; if(right<length && a[right]>a[largest]) largest=right; if(largest!=i) { swap(a,i,largest); MaxHeapify(a,length,largest);//递归调用 }}void BuildMaxHeap(int *a,int length)//建堆{ for(int i=length/2;i>=1;i--) { MaxHeapify(a,length,i); }}int main(){ int k[]={0,1,3,5,6,7,9,1,100}; int length=sizeof(k)/sizeof(int); int countnum=3;//要排列前countnum个最小的数 BuildMaxHeap(k,countnum);//先建一个大小为countnum=3的最大堆;则对顶元素k[1]为最大 for(int i=0;i<length-countnum;i++) { if(k[1]>k[i+countnum])//继续从countnum遍历k数组countnum后面的数,如果比堆顶元素还大,则更新堆 { k[1]=k[i+countnum]; MaxHeapify(k,countnum,1);//注意,这里更新堆输入的长度依然是countnum } } for(int jj=0;jj<countnum;jj++)//打印堆内的元素 printf("the %d num is %d\n",jj,k[jj]); return 0;}
参考网址:
经典排序算法 - 选择排序Selection sort
程序员编程艺术:第三章、寻找最小的k个数
- 数据结构学习笔记5-寻找最小的k个数(选择排序和堆排序)
- 快速排序-随机选择,寻找最小的第K个数
- 堆排序(最小的K个数)
- 【java】堆排序 最小的k个数
- 数据结构学习笔记 --- 排序(选择排序、堆排序)
- 数据结构学习笔记 --- 排序(选择排序、堆排序)
- 用堆排序寻找数组中最大的K个数
- 快速排序及寻找最小的k个数
- 九度OJ 1371 最小的K个数 -- 堆排序
- 【剑指offer】 堆排序查找最小的K个数
- 编程之美 -- 2.5 寻找最大的K个数(堆排序方法)
- 寻找数组中最小的k个数 "最小堆方法"
- 寻找最小的k个数(先快速排序,然后输出前k个元素)
- 最小的K个数 (冒泡和最小堆)
- 算法学习(一):寻找最小的k个数
- (3)寻找最小的K个数
- 寻找数组中最小的k个数(快排和堆排)
- 寻找数组中最小的k个数(快排和堆排)
- postgresql 导数据时无需输入密码设置
- Swift-循环
- android 的屏幕适配
- 手机脱离电脑持续抓LOG教程
- Single Number II LeetCode
- 数据结构学习笔记5-寻找最小的k个数(选择排序和堆排序)
- OC多文件开发简单例子实现(重点:self在对象方法中调用其他对象方法的3种方法)
- android.app.Activity 的介绍
- Swift-字符串
- Swift-数组
- 8.18总结
- 设置win7的MTU
- Swift-字典
- 搜索栏UISearchBar和UISearchController