剑指offer之面试题:查找和排序
来源:互联网 发布:管道安装设计软件 编辑:程序博客网 时间:2024/05/23 20:40
二分查找
基本思想:将有序序列等分为几乎相等的两部份,待查关键字和划分元比较。如果小于划分元,则递归处理左半部分;否则处理右半部分。
非递归算法:
BinarySearch1(L[],n,x){ left=1; right=n; flag=0; while(left<=right&&flag==0){ mid=(left+right)/2; if(x==L[mid]){ flag=1; } else if(x>L[mid]){ left=mid+1; } else{ right=mid-1; } } if(flag==1)return mid; else return -1;}
递归实现
BinarySearch2(L[] ,x,i,j){ if(i>j)return -1; if(i==j){ if(L[i]==x)return i; else return -1; } else{ mid=(i+j)/2; if(x==L[mid)return mid; else if(x<L[mid])return BinarySearch2(L,x,i,mid-1); else return BinarySearch2(L,x,mid+1,j); }}
java实现
/** * */package com.su.biancheng;/** * @title BinarySearch.java * @author Shuai * @date 2016-4-16上午11:34:58 */public class BinarySearch { public static int BinarySearch(int[] array,int x){ /*if(array==null||array.length<=0) return -1;*/ int left=0; int right=array.length-1; int flag=0; int mid=0; while(left<=right&&flag==0){ mid=(left+right)/2; if(x==array[mid]) flag=1; else if(x<array[mid]) right=mid-1; else left=mid+1; } if(flag==1) return mid; return -1; } public static int BinarySearch2(int[] array,int x,int left,int right){ if(left>right) return -1; if(left==right){ if(x==array[left]) return left; else return -1; } else{ int mid=(left+right)/2; if(x==array[mid]) return mid; else if(x<array[mid]) return BinarySearch2(array,x,left,mid-1); else return BinarySearch2(array,x,mid+1,right); } } public static void main(String[] args){ int[] array={1,2,3,3,5,5,7,8,9}; int x=5; System.out.println(BinarySearch(array,x)); System.out.println(BinarySearch2(array,x,0,array.length-1)); }}
排序
快排序
期望时间为O(nlogn)
基于比较的排序时间下界为logn!=nlogn-1.44n+O(logn)
快排序平均为1.39nlogn+O(n)
1 算法描述
(1) 方法:分治法
分解:A[p..r]==>A[p..q-1]<=A[q]<A[q+1..r]
递归:递归对A[p..q-1]和A[q+1..r]进行快速排序,
临界条件:区间长度为1,空操作
合并:空操作,子数组原址排序,不需要合并
(2) 算法
QuickSort(A,p,r){ if(p<r){ q=partition(A,p,r); QuickSort(A,p,q-1); QuickSort(A,q+1,r); }}partition(A,p,r){ x=A[r]; i=p-1; for(j=p;j<r;j++){ if(A[j]<=x){ i++; swap(A[i],A[j]); } } swap(A[i+1],A[r]); return i+1;}
2 性能分析
(1)最坏的划分:A[p..q-1],A[q+1..r]中有一个区间是空的
T(n)=T(n-1)+T(0)+O(n)=T(n-1)+O(n)=O(n^2)
(2)最好的划分:一分为二,每个区间长度大致相等
T(n)=2T(n/2)+O(n)=O(nlogn)
(3)平衡划分:每次划分产生的区间为9:1(固定比例)
可用递归数得到
T(n)<=cn*h=cn*log(10/9)n=O(nlogn)
3 随机化版本:利用随机数发生器,随机产生划分元
将QuickSort算法中的partition改为
RandomizedPartition(A,p,r){ i=random(p,r); Swap(A[i],A[r]); return pratition(A,p,r);}
4 期望时间O(nlogn),证明看算法导论P101,7.4快速排序分析
快排序平均性能最优,但不是任何时候都是最优。如数组本身有序,而每一轮的排序都是以最后一个元素作为划分元,此时时间为O(n^2)。面试的时候,如果面试官要我们实现一个排序算法,首先要清楚这个排序的应用环境是什么、有哪些约束条件,然后在选择合适的排序算法。举个例子:
面试官:请实现一个排序算法,要求时间效率是O(n)应聘者:对什么数字进行排序,有多少数字?面试官:我们想对公司的员工的年龄排序,我们公司总共有几万名员工应聘者:也就是数字的大小在一个较小的范围内,对吧?面试官:恩,是的应聘者:可以使用辅助空间吗?面试官:看你用多少辅助内存,只允许使用常量大小的辅助空间,不得超过O(n)
根据以上交谈,不难想到时间O(n)的排序,常见的三种:
计数排序:n个输入元素的每一个都是在到k区间内的一个整数,k是某个整数,排序时间为O(n+k),k=O(n)时,O(n)。
基数排序:计数排序的一个扩展,非负整数,k进制表示不超过d位数。k为基,d为位数,时间为O(d(n+k)),k=O(n)且d为常数时,O(n)。
桶排序:输入是均匀分布在[0,1)上的实数。
符合题意的计数排序。
计数排序
1 基本思想
统计<=A[i]的元素数目,将A[i]置入相应位置,即A[i]–>B[<=A[i]的元素数目],主要解决的问题:
q1:计数,统计小于或等于A[i]的元素数目
q2:值相同元素的处理
2 特殊情形的计数排序
问题描述:n个互补相同的整数A[1..n],1<=A[i]<=n
算法:
SpecialCountingSort(A,B){ //B[1..n]为排序结果 for i=1 to n do{ B[A[i]]=A[i]; }}
3 一般情形的计数排序
问题描述:n个可以相同的整数A[1..n],1<=A[i]<=k
基本思想:A[1..n]–>计数器C[1..k]–>B[1..n]
s1:值相同元素计数:将A中值为i的元素个数计入C[i]中
s2:累计计数:对C[1..k]进行修改,使得C[i]的值表示为<=i的元素个数
s3:放置:将A[i]依据C[A[i]],放入正确的位置B[C[A[i]]],并修改C[A[i]]=C[A[i]]-1
算法:
CountingSort(A,B,k){ //let c[1..k] to be a new array for i=0 to k{ C[i]=0; } for j=1 to A.length{ C[A[j]]=C[A[j]]+1; } //C[i] now contains the number of elements equal to i for i=1 to k{ C[i]=C[i]+C[i-1]; } //C[i] now contains the number of elements less than or equal to i for j=A.length downto 1{ B[C[A[j]]]=A[j]; C[A[j]]=C[A[j]]-1; }}
演示例子可以参考算法导论P109图8-2
根据对技术排序的分析,可以写出上面面试官的年龄排序的代码
void SortAges(int[] ages,int length){ if(ages==null||ages.length<=0) return; int oldestAge=99; int timesOfAge[oldestAgs+1]; for(int i=0;i<=oldestAge;i++){ timesOfAge=0; } for(int i=0;i<length;i++){ int age=ages[i]; if(age<0||age>oldestAge) System.out.println("age out of range"); ++timesOfAge[age]; } int index=0; for(int i=0;i<=oldestAge;i++){ for(int j=0;j<timesOfAge[i];j++){ ages[index]=i; index++; } }}
基数排序
假定A[1..n]是非负整数,用k进制表示不超过d位
算法:
RadixSort(A,d){ for i=1 to d do{ 使用稳定的排序算法对A的第i位排序,如计数排序 }}时间T(n)=O(d(n+k))//k为基,d为位数=O(n)//如果k=O(n)且d为常数
演示例子见算法导论P110图8-3
问题?若d不为常数,基数排序算法还是线性时间吗?
设n个整数的取值范围是0-n^c,c是常数,c>=1
对于十进制数,n^c需要的位数d=log(10)n^c+1==log(10)n
T(n)=O(d(n+k))=O(nlogn)//k=10不是线性时间
算法何时为线性时间?
Idea:只要是d变为常数,k变大到与n同阶how to do:选基k=n,则n^c的位数Log(n)n^c=c=dd=c,k=n,T(n)=O(n)
桶排序
基本思想:
假定:输入是均匀分布在[0,1)上的实数
s1:[0,1)划分为[0,1/n),[1/n,2/n),..,[k/n,(k+1)/n),[(n-1)/n,1)n个大小相等的子区间,每个子区间看做一个桶
s2:将n个元素分配到桶中
s3:对每个桶里的元素进行排序,依次连接桶
算法思想:
输入0<=A[1..n]<1
辅助数组B[0..n-1]是一个指针数组,指向每个桶
关键字映射:由于0<=A[1..n]<1,必须将A[i]映射到0,1,..,n-1上
因为[0,1)-->[0,n) //nA[i]即k<=nA[i]<k+1 //存在k所以桶号k=nA[i]
算法:
BucketSort(A){ time n=A.length; for i=1 to n do{ O(n) 将A[i]插入到链表B[nA[i]]中; } for i=0 to n-1 do{ O(n)* 用插入排序将B[i]排序; } 将B[0],B[1]..B[n-1]连接起来 O(n)}O(n)*因为n个数量是均匀分布在[0,1)中所以每个桶中大约只有一个数,故时间为O(n)
- 剑指offer之面试题:查找和排序
- 剑指offer面试题3之二维数组中的查找
- 剑指offer之面试题3:二维数组的查找
- 剑指offer之面试题3:二维数组中的查找
- 剑指Offer之面试题3:二维数组中的查找
- 《剑指Offer》面试题三之二维数组中的查找
- 剑指Offer之面试题3:二维数组中的查找
- 剑指Offer之面试题28:字符串的排序
- 【剑指offer】2.4.1查找和排序——面试题8:旋转数组的最小数字
- 剑指Offer面试题8旋转数组的最小数字(二分查找)附带快排和按年龄排序
- 剑指offer面试题之二维数组查找——二分查找的本质
- 剑指offer--面试题53 在排序数组中查找数字(二分)
- PHP算法面试题 排序和查找
- 剑指offer面试题java实现之题3:二维数组中的查找
- 剑指Offer学习之面试题3 :二维数组中的查找
- 剑指offer面试题之替换空格
- 剑指offer面试题之跳台阶
- 剑指offer面试题之扑克牌顺子
- #if defined的使用
- 24. Java IO: Reader-Writer && Java IO: PrintWriter
- X11
- 多重继承
- Android控件之ProgressBar
- 剑指offer之面试题:查找和排序
- Android控件之WebView
- Fragment 基础详解
- Struts中错误: No result defined for action
- 116. Populating Next Right Pointers in Each Node
- 实现Fragment与Activity通信
- Potato项目文档(3)
- Android之 Viewpager
- 258.[LeetCode]Add Digits