剑指offer自学系列(一)
题目描述:输入n个整数,找出其中最小的k个数,例如,输入{4,5,1,6,2,7,3,8}这8个数字,最小的4个数字是1,2,3,4
题目分析:首先我能想到的是先对数组排序,从小到大,然后直接输出想要的最小的k个点,而根据排序算法,表现的比较好的快速排序时间复杂度为o(nlogn),但是有没有时间复杂度更小的呢?在快速排序中,我们写过一个partition函数,它的作用是将比基准值小的放到数组前面,大的放到数组后面,如果小的那部分长度不满足我们要求的k值,我们通过左移或者右移知道找到我们想要的k值,代码如下:
#include<iostream>
using namespace std; int pa(int* input, int start, int end) {
int begin = start;//快排函数将数组比一个值小的值放到数组前面,大的放后面
int last = end;
int key = input[begin];
while (begin < last) {
while (begin < last&&input[last] >= key) {
last--;
}
input[begin] = input[last];
while (begin < last&&input[begin] <= key) {
begin++;
}
input[last] = input[begin];
}
input[begin] = key;
return begin;
} int *GetKLeastNum(int* input, int n, int k) {
if (input == nullptr || k > n || n <= || k <= ) {
return ;
}
int start = ;
int end = n - ;
int index = pa(input, start, end);
while (index != k - ) {
if (index > k - ) {
end = index - ;
index = pa(input, start, end);
}
else {
start = index + ;
index = pa(input, start, end);
}//如果快排基准值前面值数量小于k,那么在后面接着找基准值,否则往前面找基准值,直到找到前面有k个值的基准值
}
return input;
} int main() {
int k = ;
int n = ;
int input[] = { ,,,,,,, };
int *value;
value = GetKLeastNum(input, n, k);
for (int i = ; i < k; ++i) {
cout << value[i] << endl;
}
}
但是这样会改变数组位置,给原数组带来变化,若实际情况不允许变动,则需要重新选择新的方法:
创建一个可以装k个数的容器,输入值首先装满容器,当容器满了后,新输入的数与容器中的其他值比较,如果比最大值小,则最大值出容器,新来的值进容器,对这个容器,我们有两种想法,一个是最大堆,首先建立一个k个数的最大堆,每次拿一个数与堆顶元素比较,当这个数比堆顶元素大时,与堆顶元素交换,然后调整重新得到一个最大堆,遍历所有的数直到最小的k个值都进堆中。
n个数组成的堆高度为o(logn),和向量对应起来,有三条性质(根节点i(v)=0):
a)若节点v有左孩子,那么编号i(LeftChild(v))=2*i(v)+1
b)若节点v有右孩子,那么编号i(RightChild(v))=2*i(v)+2,
c)若节点v有父节点,那么编号i(Parent(v))=ceil[i(v)/2]-1,(ceil表示上取整)
而最大堆性质是任意节点值比它的子节点值都要大,最小堆是任意节点的值比它的子节点值都要小,通俗点理解就是最大堆从上到下任何一条路径都是从大到小排序好的,最小堆从上到下任何一条路径都是从小到大排序好的,找到前k个最小值代码如下:
#include<iostream>
using namespace std; void swap(int* x1,int* x2) {
int temp = *x1;
*x1 = *x2;
*x2 = temp;
} void AdjustDown(int* input,int k, int parent) {
if (input == NULL || k <= ) { return; }
int child = * parent + ;
while (child<k) {
if ((child + < k) && (input[child] < input[child + ]))
++child;//左节点和右节点选最大的值往上检索
if (input[child] > input[parent]) {//从当前往上排序直到堆顶
swap(&input[child], &input[parent]);
parent = child;
child = * parent + ;
}
else
break;
}
} int *GetKLeastNum(int* input, int n ,int k) {
int i = ;
for (i = ( * k - ) / ; i >= ;--i) {
AdjustDown(input, k, i);//选择数组前k个值构建一个最大堆
}
for (i = k; i < n;++i) {
if (input[i]<input[]) {//将后n-k个值一个个送入最大堆,小于最大值则最大值弹出,重新构建最大堆
swap(&input[i], &input[]);
AdjustDown(input, k, );
}
}
return input;
} int main() {
int k = ;
int n = ;
int input[] = { ,,,,,,, };
int *value;
value = GetKLeastNum(input, n, k);
for (int i = ; i < k; ++i) {
cout << value[i] << endl;
}
}
另一种是红黑二叉树,红黑二叉树主要是对2-3查找树进行编码,需要满足以下条件:
(1)二叉树树根始终为黑色
(2)外部节点均为黑色
(3)如果节点为红色,则子节点均为黑色
(4)从任何一个外部节点到根节点的沿途,黑色节点数目相等
红黑树理解起来颇为复杂,等待后续更新
参考:
http://www.cnblogs.com/skywang12345/p/3610187.html(堆与二叉堆基础知识)
https://blog.csdn.net/pursue_my_life/article/details/80253469(堆的构建以及堆排序)
https://www.cnblogs.com/edisonchou/p/4799678.html(对应本题最大堆及测试方法)
剑指offer自学系列(一)的更多相关文章
- 剑指offer自学系列(三)
题目描述: 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变,例如{5,1,4,2 ...
- 剑指offer自学系列(五)
题目描述:请实现一个函数用来找出字符流中第一个只出现一次的字符.例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g".当从该字符流中读出 ...
- 剑指offer自学系列(四)
题目描述: 输入一个正整数数组,把数组里面所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个,例如输入数组{3,32,321},输出的最小数字为321323 题目分析: 如果采用穷举法,把 ...
- 剑指offer自学系列(二)
题目描述: 在一个长度为n的数组里的所有数字都在0到n-1的范围内,数组中某些数字是重复的,但不知道有几个数字是重复的,也不知道每个数字重复几次,请找出数组中任一个重复的数字,例如,如果输入长度为7的 ...
- 剑指offer题目系列三(链表相关题目)
本篇延续上一篇剑指offer题目系列二,介绍<剑指offer>第二版中的四个题目:O(1)时间内删除链表结点.链表中倒数第k个结点.反转链表.合并两个排序的链表.同样,这些题目并非严格按照 ...
- 剑指offer题目系列二
本篇延续上一篇,介绍<剑指offer>第二版中的四个题目:从尾到头打印链表.用两个栈实现队列.旋转数组的最小数字.二进制中1的个数. 5.从尾到头打印链表 题目:输入一个链表的头结点,从尾 ...
- 剑指offer题目系列一
本篇介绍<剑指offer>第二版中的四个题目:找出数组中重复的数字.二维数组中的查找.替换字符串中的空格.计算斐波那契数列第n项. 这些题目并非严格按照书中的顺序展示的,而是按自己学习的顺 ...
- 剑指Offer对答如流系列 - 实现Singleton模式
目录 面试题2:实现Singleton模式 一.懒汉式写法 二.饿汉式写法 三.枚举 面试题2:实现Singleton模式 题目:设计一个类,我们只能生成该类的一个实例. 由于设计模式在面向对象程序设 ...
- 剑指Offer对答如流系列 - 重建二叉树
面试题6:重建二叉树 题目描述: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8} ...
随机推荐
- 从Uber司机只是合同工看,零工经济将受沉重打击?
Uber自诞生以来,就始终处于漩涡之中--与当地的同类平台斗.创始人出现负面新闻.司机不断抗议--而就在近日"流血上市"后,Uber也没能迎来好时光.反而是股价不断下跌,市值疯狂蒸 ...
- springmvc常用注解详解
1.@Controller 在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ...
- Systemverilog for design 笔记(七)
转载请标明出处 第一章 接口(interface) 1.1. 接口的概念 接口允许许多信号合成一组由一个端口表示. 1.2. 接口声明 //接口定义 Interface main_bus ...
- ClientAbortException: java.io.IOException: 您的主机中的软件中止了一个已建立的连接。,进不了Debug,访问不到方法
开发项目的时候运行报错,Debug进不去方法,猜想是js中ajax的url 路径的问题,后来找公司的斌哥解决了问题,感谢! 异常: 解决过程: 清理缓存,修改js代码,通过浏览器的F12查看,发现修改 ...
- 【快学springboot】3.多种接受参数的方式
前往掘金查看:https://juejin.im/post/5d05e5f9f265da1b6c5f74b4 前言 在开发中,我们常常会碰到多种传参的方式.这里,我们就来总结下,springboot中 ...
- 吴裕雄 Bootstrap 前端框架开发——Bootstrap 表单:表单控件大小
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- 【转载】C#常用控件属性及方法介绍
C#常用控件属性及方法介绍 目录 1.窗体(Form) 2.Label (标签)控件 3.TextBox(文 ...
- springMVC List对象转换为json列表对象
@ResponseBody @RequestMapping("xxx.do") public List<POJO> getList(){ return List< ...
- Python学习笔记001
二进制 换算: 十进制转二进制 除二取余,然后倒序排列,高位补零. 将正的十进制数除以二,得到的商再除以二,依次类推知道商为零或一时为止,然后在旁边标出各步的余数,最后倒着写出来,高位补零就可以. 二 ...
- detectron2 配置记录
1. RuntimeError: Not compiled with GPU support (ROIAlign_forward at /home/oliver/PycharmProjects/det ...