数据结构与算法分析 收获总结 第9章 检索

先根据今天课堂上老师讲的来吧
检索(search)方式主要有三种:
1.线性表,顺序表
2.哈希表,就是键值对这种
3.二分树
其中对于顺序表,如果无序的,要找出某一个数,就左到右比较每一个,时间复杂度为θ(n),如果有序的,再用左到右比较就会浪费有序的这一价值,因为也是θ(n),因此要用到二分搜索树(Binary Tree)便可充分发挥有序的这一条件,时间复杂度降为θ(logn)
二分搜索的代码(仅针对有序数组A[])

int binary(int A[],int n,int K){
 int l=-1; //左边界
 int r=n;  //右边界,注意左右边界都是恰好超出了数组边界
 while(l+1 !=r){  //循环直到l和r相遇
    int i=(l+r)/2; //折半二分
    if(K<A[i]) r=i; //查找值在左边
    if(K == A[i]) return i; //恰好找到,刚好在中间位置,直接返回下标
    if(K > A[i]) l=i; //查找值在右边
    }
  return n; //A中未找到该值,返回n
}

然后老师为充分展示检索的魅力,又到了老环节,展示一些经典面试题并讲解
1.长度为n-1的数组,其元素为1到n的不同整数且未排序,寻找其中丢失的整数,要求时间复杂度为O(n)
2.有排好序的数组A进行多次移位处理,每次移位都是吧最后的元素移到最前面去,其他元素依次后移,对A设计二分搜索算法,使能够在logn时间内查询元素x是否在里面
解析:关键是要找到最大值位置。定义三个位置,开头一个,末尾一个,还有一个mid中间位置,通过if条件判断找到最大值,最终转化为从小到大的数组的二分搜索问题
3.从未排好序的数组A中查找第K小的值
4.从未排序的数组A中查找第K小的值
5.长度为n的数组,元素取值范围[1,n],判断其中是否有重复的元素
(前5题过于简单就不说了,,,其实是上课梦周公了,没听到这5题的讲解。本想课间小憩,结果第二节课一半就没了…)
6.排好序的整数A,删除其中的重复元素,但不能使用额外的数组
解析:本想用一个额外数组,与之前一个值不同就存入,相同则不存入。既然不能使用额外的数组,就用指针来整了,在该数组中,设置两个指针,一个指针a一直往前走,不停下来,另一个指针b就用来指向不重复的元素。a走到相同的则剔除,b不操作。遇到不同的元素,a继续往下走,b指向该不同
7.两个排好序的数组A和B,查找A中哪些元素在B中
解析:用双指针,A数组开头用指针a指向,B数组开头用指针b指向,a,b指向的数进行比较,谁的数小,谁(该指针)往后一位,再比较。a,b指向相同则两指针同时走,并且就说明找到了相同值
8.排好序的整数数组A,长度为n,给定整数X,问A中是否有两个值,其和为X,时间复杂度为O(n)
解析:这题老师也糊涂了,说需要课后吃碗小面再思考下…只讲了nlogn的方法,就是再开一个数组,记录X-A,再在该数组中对A的每一个值二分查找是否存在X-A…

这周作业:(1)证明如果无向图的边权重各不相同,其最小生成树是唯一的。
即证明如果有边 e1,e2,e3,e4, 其权重满足w1 < w2 < w3 < w4 且w2+w3 = w1+w4,不存在两种最小生成树,一种仅包含e2和e3,另一种包含e1和e4。

设G是所有边权均不相同的无向联通图。
证明一:
首先,易证图G中权值最小的边一定是最小生成树中的边。(否则最小生成树加上权值最小的边后构成一个环,去掉环中任意一条非此边则形成了另一个权值更小的生成树)。
之后用反证法,假设G存在俩个不同的最小生成树
①.设G的俩个不同的最小生成树T1 T2,设这俩颗生成树的并集为子图G1,G1为连通图且T1 T2显然为G1的最小生成树,由首先可得知俩颗生成树至少包含一条公共边,将G1中两颗生成树的公共边删去,得到子图G2。G2由一个或多个连通分量组成,其中至少有一个连通分量的最小生成树不唯一(否则若所有连通分量的最小生成树唯一,则将删掉的公共边加上,则T1等于T2,这与假设相矛盾)。
②.对其中一个最小生成树不唯一的连通分量设为H,若H中点数>2,重复①的操作。否则H中只有俩个点,由于所有边权值不同,显然最小生成树唯一,这与①中的最后一句相矛盾。
综上,所有边权均不相同的无向图最小生成树是唯一的。

证明二:
设T,T’为G的俩个最小生成树,设T的边集E(T)={e1,e2,…,em},T’的边集E(T’)={e’1,e’2,…,e’m}。
设ek满足ek≠e’k且k最小,由于所有边权值不同,不妨假设weight(ek)

证明三:
假设它存在两颗最小生成树a, b我们找到这两个方案不同的边中最小的一条边x, (既x在一个生成树上,不在另一个生成树上)。假设x在a里面,那我们考虑把x塞到b里面去,这样b+x里面肯定有一个环,环上至少有一条边比x大(如果每条边都比x小,那根据x的定义这些边肯定也都在a里面,那a就有环了,矛盾),把这条边去掉之后还是一个生成树(你考虑把环去掉一条边变成了链,但是原来联通的现在也还是联通的)。但是这颗生成树比b小(加进了一条x,去掉了一条比x大的边),所以b不是最小生成树,矛盾故最小生成树是唯一的
设G是所有边权均不相同的无向联通图。

(2)排好序的数组int A[n], 用二分法查找包含在区间 [a,b)的元素个数 (a < b)
思路:就是分别二分查找元素a的下标,b的下标,然后下标相减就可得。 如果数组未排好序就要用冒泡排序,再进行二分查找。

#include
using namespace std;
int solve(int A[],int n,int a,int b) 
{
 int less,greater;
 int l=-1; //二分查找a的下标
 int r=n;  
 while(l+1 !=r){ 
    int i=(l+r)/2; 
    if(a<A[i]) r=i; 
    if(a == A[i]) { less = i; break; }//找到后用less记录
    if(a > A[i]) l=i; 
    }

 l=-1; //二分查找b的下标
 r=n;  
 while(l+1 !=r){ 
    int i=(l+r)/2; 
    if(b<A[i]) r=i; 
    if(b == A[i]) { greater = i; break; }//找到后用greater记录
    if(b > A[i]) l=i; 
    }
int amount=greater - less;
return amount;
}

int main(){
	int A[6]={1,2,3,4,5,6};
	cout<<solve(A,6,1,5)<<endl;  //结果为4 
}

这种写法运行时间快很多:

#include
using namespace std;

int BinarySearch(int A[],int n,int k) 
{
 int l=-1; //二分查找下标
 int r=n;  
 while(l+1 !=r){ 
    int i=(l+r)/2; 
    if(k<A[i]) r=i; 
    if(k == A[i]) return i;//记录
    if(k > A[i]) l=i; 
    }
    return k; //未找到
}
int solve(int A[],int n,int a,int b){
 return BinarySearch(A,6,b)-BinarySearch(A,6,a);
}
int main(){
	int A[6]={1,2,3,4,5,6};
	cout<<solve(A,6,1,5)<<endl;  //结果为4 
}

这一章通过考题来看的话,主要就是考散列函数,而且考的也很简单,就是怎样把数放到槽中

你可能感兴趣的:(数据结构)