算法系列笔记2(静态表顺序统计-随机选择算法)

问题:当给定存在静态表(如数组)中的n个元素,如何快速找到其中位数、最小值、最大值、第i小的数?

首先想到的方法是先对数组元素进行排序,然后找到第i小的元素。这样是可行的,但比较排序最快也需要O(nlgn),能否在线性时间内解决呢。这就是随机的分治法—随机选择。

思想:利用随机划分(在快速排序中介绍过)找到主元r,这样就将小于等于r的元素放在了其左边,大于r的元素放在了其右边。这是可以计算出r的rank为k,如果正好等于i,则就返回该元素;如果k大于i,则在左边中寻找第i小的元素,否则在右边中寻找第i-k小的元素。

#include <iostream>
#include <ctime>
using namespace std;

void swap(int &t1, int &t2){
	int tmp = t1;
	t1 = t2;
	t2 = tmp;
}

int randomPartition(int r1[], int p, int q){
	srand((unsigned)time(0));
	int randInt = rand()%(q-p+1)+p;          // 随机选择一个元素作为主元
	swap(r1[p], r1[randInt]);

	int i = p, j = p+1;
	int pivot = r1[p];
	for(j = p+1; j <= q; j++){
		if(r1[j] < pivot){
			i++;
			swap(r1[i], r1[j]);
		}
	}
	swap(r1[i], r1[p]);
	return i;

}

// 随机选择算法  找到n elements, the kth smallest element
int randomSelect(int r1[], int p, int q, int i){
	if(p == q) return r1[p];
	int r = randomPartition(r1, p, q);
	int k = r - p + 1;        // 划分r1[r]左边元素个数 包括r1[r]
	if(i == k) return r1[r];
	if(i < k) return randomSelect(r1, p, r-1, i);     // 在左边寻找第i小元素
	else return randomSelect(r1, r+1, q, i-k);        // 在右边寻找第i-k小元素
}

int main(){
	int a[10] = {3,10,2,2,7,8,4,8,8,19};
	for(int i = 1; i <= 10; i++){
		cout << randomSelect(a, 0 ,9, i) << endl;
	}
	return 0;
}

将排名前10的结果都输出来了

通过分析可以得出该算法的时间复杂度为O(n),当然最坏的情况为O(n^2).

时间: 2024-12-20 14:56:19

算法系列笔记2(静态表顺序统计-随机选择算法)的相关文章

算法系列笔记5(扩展数据结构-动态顺序统计和区间树)

在编程中,我们往往使用已有的数据结构无法解决问题,这是不必要急着创建新的数据结构,而是在已有数据结构的基础上添加新的字段.本节在上一次笔记红黑树这一基础数据结构上进行扩展,得出两个重要的应用-动态顺序统计和区间树. 动态顺序统计 在算法系列笔记2中我们在线性时间内完成了静态表的顺序统计,而这里我们在红黑树上进行扩展,在O(lgn)时间内完成该操作,主要包括返回第i 排名的元素os_select(i)和给定一个元素x,返回其排名(os_rank(x)). 思想:添加新项:在红黑树的结点上记录下该结

Randomize select algorithm 随机选择算法

从一个序列里面选择第k大的数在没有学习算法导论之前我想最通用的想法是给这个数组排序,然后按照排序结果返回第k大的数值.如果使用排序方法来做的话时间复杂度肯定至少为O(nlgn). 问题是从序列中选择第k大的数完全没有必要来排序,可以采用分治法的思想解决这个问题.Randomize select 算法的期望时间复杂度可以达到O(n),这正是这个算法的迷人之处.具体的算法分析可以在<算法导论>这本书里查看. 贴出伪代码: RANDOMIZED-SELECT(A, p, r, i) 1 if p =

随机选择算法

随机选择算法和快速排序原理相似,所以有时候也称作“快速选择算法”,一般选择问题可以证明都能在O(n)时间内完成.随机选择算法的期望运行时间为线性时间,即Θ(n),但其最坏情况运行时间为O(n^2).最坏情况与快排一样,都是运气不好导致划分不均匀. 代码: #include "stdafx.h" #include <iostream> #include <vector> #include <stdlib.h> class QuicklySelect {

算法系列笔记1(排序)

本次主要记录一些经典的排序算法,其中包括冒泡排序.直接选择排序.插入排序.归并排序.快速排序.堆排序.希尔排序.桶排序以及计数排序和基数排序.首先会给出这些排序算法的基本思想,然后给出实现的代码,最后会给出其时间复杂度. 1:冒泡排序 思想: (1):比较相邻的前后两个元素,如果后面的数据小于前面的数据,则交换这两个数据的位置.这样经过一次遍历,最小的元素将在第0个位置,属于"冒泡". (2):重复第一步,依次将第二小-的元素排列到数组的顶端. // 交换数据的三种方法 void sw

查找算法系列之简单查找:顺序查找、二分查找、分块查找

近期总结了各大排序算法的原理 ,并对其进行了实现,想着一并把查找算法总结了,今天就着手开始总结查找算法. 废话不多说,这篇文章从最简单的查找算法开始讲起,之后会补充复杂的二叉搜索树查找(BST)和B树,B+树查找以及哈希查找等. 顾名思义,查找就是寻找到关键字在队列中的位置,最笨的查找算法就是依次顺序比较,复杂度为O(n),但是有很多方法的复杂度可以达到O(logn)等等. 1.顺序查找 关键字与数组中的数顺序比较,时间复杂度O(n). template<class T> int OrderS

[算法系列之二十六]字符串匹配之KMP算法

一 简介 KMP算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特-莫里斯-普拉特操作(简称KMP算法).KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的. 二 基于部分匹配表的KMP算法 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含搜索串"ABCDABD"? 步骤1:字符串"BBC ABC

算法系列笔记8(有关图的算法二—最短路径问题)

图的最短路径问题主要分为两类,单源最短路径问题和全对最短路径问题.单源最短路径问题指给点单个源点,求其到所有其它顶点之间的最短距离.而全对最短路径问题指所有顶点之间的最短路劲问题.此外对于单对最短路径问题,从渐进意义上来看,目前还没有比最好的单元算法更快的算法来解决这一问题. 一:单源最短路径问题 单源最短路劲问题根据其权重分为四类,当图G=(V,E)为无权图,直接使用广度优先遍历(这里不做介绍):当权值为非负值,则使用Dijkstra算法:存在负权值及负权环,可以使用Bellman-Ford算

算法系列笔记6(有关图的算法一—搜索,拓扑排序和强连通分支)

简单概念:对于图G(V,E),通常有两种存储的数据结构,一种是邻接矩阵,此时所需要的存储空间为O(V^2):第二种是邻接表,所需要的存储空间为O(V+E).邻接表表示法存在很强的适应性,但是也有潜在的不足,当要快速的确定图中边(u,v)是否存在,只能在顶点u的邻接表中搜索v,没有更快的方法,此时就可以使用邻接矩阵,但要以占用更多的存储空间作为代价:此外当图不是加权的,采用邻接矩阵存储还有一个优势:在存储邻接矩阵的每个元素时,可以只用一个二进位,而不必用一个字的空间. 图的搜索算法 搜索一个图示有

算法系列笔记3(二叉查找树)

(1)二叉查找树的性质:设x为二叉查找树的一个结点.如果y是x左子树中的一个结点,则key[y]≤key[x].如果y是x的右子树中的一个结点.则key[x]≤key[y]. (2)二叉查找树的结点中除了key域和卫星数据外,还包括left.right和p分别指向结点的左儿子.右儿子和父节点. (3)构造一棵二叉查找树最好情况下时间复杂度为O(nlgn),最坏情况为O(n^2).随机化构造一棵二叉查找树的期望时间O(nlgn).与快排和随机化快速排序算法是做相同的比较,但是顺序不一样.可以证明随