题目描述:输入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自学系列(一)的更多相关文章

  1. 剑指offer自学系列(三)

    题目描述: 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变,例如{5,1,4,2 ...

  2. 剑指offer自学系列(五)

    题目描述:请实现一个函数用来找出字符流中第一个只出现一次的字符.例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g".当从该字符流中读出 ...

  3. 剑指offer自学系列(四)

    题目描述: 输入一个正整数数组,把数组里面所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个,例如输入数组{3,32,321},输出的最小数字为321323 题目分析: 如果采用穷举法,把 ...

  4. 剑指offer自学系列(二)

    题目描述: 在一个长度为n的数组里的所有数字都在0到n-1的范围内,数组中某些数字是重复的,但不知道有几个数字是重复的,也不知道每个数字重复几次,请找出数组中任一个重复的数字,例如,如果输入长度为7的 ...

  5. 剑指offer题目系列三(链表相关题目)

    本篇延续上一篇剑指offer题目系列二,介绍<剑指offer>第二版中的四个题目:O(1)时间内删除链表结点.链表中倒数第k个结点.反转链表.合并两个排序的链表.同样,这些题目并非严格按照 ...

  6. 剑指offer题目系列二

    本篇延续上一篇,介绍<剑指offer>第二版中的四个题目:从尾到头打印链表.用两个栈实现队列.旋转数组的最小数字.二进制中1的个数. 5.从尾到头打印链表 题目:输入一个链表的头结点,从尾 ...

  7. 剑指offer题目系列一

    本篇介绍<剑指offer>第二版中的四个题目:找出数组中重复的数字.二维数组中的查找.替换字符串中的空格.计算斐波那契数列第n项. 这些题目并非严格按照书中的顺序展示的,而是按自己学习的顺 ...

  8. 剑指Offer对答如流系列 - 实现Singleton模式

    目录 面试题2:实现Singleton模式 一.懒汉式写法 二.饿汉式写法 三.枚举 面试题2:实现Singleton模式 题目:设计一个类,我们只能生成该类的一个实例. 由于设计模式在面向对象程序设 ...

  9. 剑指Offer对答如流系列 - 重建二叉树

    面试题6:重建二叉树 题目描述: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8} ...

随机推荐

  1. 设计模式课程 设计模式精讲 13-2 享元模式coding

    1 代码演练 1.1 代码演练1 1 代码演练 1.1 代码演练1 需求: 每周由随机部门经历做报告: 重点关注: a 该案例是单例模式和享元模式共同使用 b 外部传入的department是外部状态 ...

  2. 118、Java中String类之取字符串长度

    01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...

  3. 利用kali自带的msfvenom工具生成远程控制软件(木马)

    2.生成一个简单的木马 3. 4. 5. 6.接下来生成的winx64muma.exe实际演示 7.将生成的winx64muma.exe在受害者机器上运行 8.在kali下输入msfconsole 9 ...

  4. sqlserver 取数据常用

    sqlDataReader: public SqlDataReader GetAuth_CourtListByAuth(int autIntNo) { // Create Instance of Co ...

  5. 题解 CF165D 【Beard Graph】

    思路:将黑边标记为1,白边标记为100000,树链剖分 如果查询时ans超过100000,那就有白边,输出-1,不然直接输出ans #include<bits/stdc++.h> #def ...

  6. CF6

    A A 不解释 #include<bits/stdc++.h> using namespace std; namespace red{ inline int read() { int x= ...

  7. Linux下如何拷贝整个目录下的所有文件

    分类: Linux使用2014-01-14 13:38 1449人阅读 评论(0) 收藏 举报 如何在Linux下拷贝一个目录呢?这好像是再如意不过的问题了.比如要把/home/usera拷贝到/mn ...

  8. 吴裕雄--天生自然JAVA面向对象高级编程学习笔记:对象的多态性

    class A{ // 定义类A public void fun1(){ // 定义fun1()方法 System.out.println("A --> public void fun ...

  9. django+centos+mariadb读写分离完美实现(上)-mysql主从备份实现

    首先画图一张,用来展示今天要做的事情,读写分离,个人理解就是使用mysql主从备份的原理,让两个数据库同时为自己提供服务.其中主库负责数据保存,从库负责数据展示,可以一主一从,也可以一主多从.从而降低 ...

  10. 2017 青岛网络赛 Chenchen, Tangtang and ZengZeng

    Chenchen, Tangtang and ZengZeng are starting a game of tic-tac-toe, played on a 3 × 3 board. Initial ...