数据结构二轮复习大题(含暴力)

数据结构二轮复习大题(含暴力)

2.2.3 01

//20页
//搜索整个顺序表,查找最小值元素并记住其位置,用结束后用最后一个元素补空
//z最小值得位置
bool Del_Min(Sqlist &L,ElemType &value) {
	if(L.length==0)
		return flase;
	//设置最小值
	value=L.data[0];
	for(int i=1;i<=length;i++)
	{
		if (L.data[i])<value)
		{ 
			value=L.data[i];
			pos=i;
		}
	}
	L.data[pos]=L.data[L.length-1]; //用最后一个元素实现覆盖,及删除 
	L.length--;
	return ture; 
	}	
}

2.2.3 02

设计一个高效算法,将顺序表L的所有元素逆置,要求算法的空间复杂度为0(1)。

  • 暴力就是再整一个数组,原数组从末尾循环遍历放到新数组里

  • 要求空间复杂度O(1)的话,从头循环到中间,交换头尾元素

  • 偶数或者奇数个元素,循环到中间,都是 < length/2,因为如果长度为4或者5,都是循环到下标为1即停,为5的时候最中间的元素(下标为2)不需要移动。

  • 时间复杂度O(n)

  • void reverse(Sqlist &list){
    	//定义一个辅助变量
    	Element temp; 
    	for(int i=0;i<length/2;i++)
    	{ //交换两个元素的位置
    		temp=L.data[i];
    		L.data[i]=L.data[length-i-1];
    		L.data[length-i-1]=temp;
    	}
    } 
    
    

2.2.3 03

03.对长度为n的顺序表L,编写一个时间复杂度为0(n)、空间复杂度为0(1)的算法,该算法删除线性表中所有值为x的数据元素。

  • 暴力就是再整一个数组,循环遍历把不等于x的元素都放入新数组
  • 时间复杂度O(n),空间复杂度O(1)就要求一次循环内解决
  • 把所有等于x的元素都扔到最后,然后 length 减掉就可以了
  • 也就是把所有不等于x的元素扔到前面,后面自然就是等于x的元素了
void del_x(SqList &list ,int x){
    //空间复杂度o(1)是指要在一个循环内搞定
	int k=0;
	for (int i=0;i<list.length;i++) {
		if (list.data[i]!=x){
			list.data[k]=list.data[i];
			k++;
		}
	}
	//新建了一个数组去接收不等于x的值
    //后面的值直接不要了;
	 list.length=k; 
	
} 

//方法二 类似与快速排序
一次循环 -> 双指针
类似快排,从两端向中间移动,将左边的x与右边的非x交换
    void del_x2(SqList &list ,int x){
	int i=-1,j=list.length,k=0;
	while(i<j){
		//list.data 原来数组 
		while(list.data[++i]!=x);
		while(list.data[--j]==x) k++;
		if (i<j){
			swap(list.data[i],list.data[j]);
			k++;
		} 
		list.length=k;
	
	}	
    

2.2.3 04 05

顺序表有序表这个答案都能用

对长度为n的顺序表L,编写一个时间复杂度为O(n)、空间复杂度为0(1)的算法,该算法删除线性表中所有值为x的数据元素。

//有序表删除的必然是s,t之间的所有元素 
void del_st(Sqlist &list,int s,int t){
	if (s>=t||)length==0){
		return false; 
	}
	int k=0;
	for(int i=0;i<length;i++ ){
		if(list.data[i]<s||list.data[i]>t){
			list.data[k++]=list.data[i];
		}
	}
    //直接不用管后面的值 
	list.length=k;
} 

//王道书上答案狗都不看
本题与上题的区别,因为是有序表,所有删除的元素必须是相连的整体。从前向后扫描顺序表L,用k记录下元素值在s到t之间的元素的个数。若其值不在s到t之间,则前移k个位置;否则执行k++.
*/
bool Del_s_t(SqList &L,int s,int t)
{
    int i,k=0;
    if(L.length==0||s>=t)
        return false;
    for(i=0;i<L.length;i++)
    {
        if(L.data[i]>=s && L.data[i]<=t)
            k++;
        else
            L.data[i-k] = L.data[i];//当前元素前移k个位置
    }
    L.length -= k; //长度减小
    return true;
}

2.2.5 06

有序顺序表中删除所有值重复的元素,使所有元素的值均不同

  • 有序列表 ➡️ 相同元素排列在一起
  • 暴力,新开一个数组,将不同元素存入
  • 需要两个指针分别操作两个数组
  • 时间复杂度O(n),空间复杂度O(n)
void del_same(SqList &list) {
  if (list.length == 0) return;
    // 1.新开一个数组
  SqList copied = list; //把整个数组都复制过来了 
  copied.data[0] = list.data[0];   //是不是多余,把数据部分也复制过来
  int k=0;
  for(int i=1;i<list.length;i++)
  {
  	if(list.data[k]!=list.data[i])
  	{
  		copied.data[++k]=list.data[i];
	}
	copied.length=k+1;  //确定长度之后后面部分就不要了 
	list=copied;
  }  
} 
仔细想想,其实并不需要两个数组,双指针就可以
前一个存储,后一个判断
void del_same2(SqList &list) {
  if (list.length == 0) return;
  //利用双指针进行判断
  int k=0;
  for (int i=1;i<list.length;i++)
  {
  	//i就是靠后的那个指针 
  	//把不相等的直接,放在下一个位置 
  	if(list[k]!=list.data[i]){
  		list,data[++k]=list.data[i]; 
	  }
  } 
  list.length = k + 1;
}

2.2.5 07

2.1.6 两个有序顺序表合并为一个新的有序顺序表,由函数返回结果顺序表

//两个表合并问题 

Sqlist merge(Sqlist A,Sqlist B){
	Sqlist C;
	if(A.length+B.length >MaxSize){
		return flase;
	}
	int i=0; j=0; k=0;
	while (i<A.length&&j<B.length){
		if (A.data[i]<B.data[j])
		{
			C.data[k++]=A.data[i++]; 
		}
		else
		{
			C.data[k++]=A.data[j++];
		}
	} 
	//2.剩下的结果全部加入表中,两个循环只会有一个运行
	//i,j 记录的是键盘的较短那个元素结束的值 
	while(i<A.length) 
		C.data[k++]=A.data[i++];
	 while (j < B.length) 
    	C.data[k++] = B.data[j++];
    	
      // 3.返回结果表
    C.length = k;
    return C;
		 
}

补充一下归并排序的代码

void Merge(int A[], int left, int mid, int right)             //合并操作的代码实现
{
	int *B = new int[right - left + 1];                       //申请一个辅助数组B[],与传递过来的序列数组等长
	//图中的三个辅助标记(工作指针)
	int i = left;                                             //指向待排序子序列数组A[left:mid]中当前待比较的元素
	int j = mid + 1;                                          //指向待排序子序列数组A[mid+1:right]中当前待比较的元素
	int k = 0;                                                //k指向辅助数组B[]中待放置元素的位置

	while (i <= mid && j <= right)                             //当i和j都指向未超过数组范围的时候
	{                                                          //从小到大排序,将A[i]和A[j]中的较小元素放入B[]中
		if (A[i] <= A[j])                                      //当前半部分数组A[left:mid]的值不大于后半部分数组A[mid+1:right]的值时,将前半部分数组辅助标记对应的值存入辅助数组中(具有稳定性)
			B[k++] = A[i++];                                   //存入辅助数组B[]中,且与之对应的辅助标记后移
		else                                                   //否则将后半部分辅助标记对应的值存入B[]中
			B[k++] = A[j++];                                   //存入辅助数组且与之对应的辅助标记后移
	}

	while (i <= mid)                                           //对序列A[left:mid]剩余的部分依次进行处理,与图中的(5)对应
		B[k++] = A[i++];                                       //将辅助标记对应的值存入辅助数组中且辅助标记后移

	while (j <= right)                                         //对序列A[mid+1:right]剩余的部分依次进行处理
		B[k++] = A[j++];                                       //将辅助标记对应的值存入辅助数组中且辅助标记后移  

	for (i = left, k = 0; i <= right; i++)                     //将合并后的序列复制到原来的A[]序列
		A[i] = B[k++];

	delete[] B;

    
递归调用这个过程
    //递归形式的归并排序算法
void MergeSort(int A[], int left, int right)                   //归并排序
{
	if (left < right)                                          //当数组内的元素数大于1时进行二分操作,只有一个元素的时候,不作任何处理直接结束
	{
		int mid;
		mid = (left + right) / 2;                              //计算中间位置
		MergeSort(A, left, mid);                               //对数组A[left:mid]中的元素进行归并排序
		MergeSort(A, mid + 1, right);                          //对数组A[mid+1:right]中的元素进行归并排序
		Merge(A, left, mid, right);                            //进行合并操作
	}
}

2.2.5 08

2.1.7 一维数组 A[m+n] 中将两个顺序表m和顺序表n 的位置互换

已知在一位数组A[m+n]中依次存放两个线性表(a1,a2,a3,…,am)和(b1,b2,b3,…bn)。编写一个函数,将数组中两个顺序表的位置互换,即将(b1,b2,b3,…bn)放在(a1,a2,a3,…,am)的前面。
算法思想:先将数组A[m+n]中的全部元素(a1,a2,a3,…,am,b1,b2,b3,…,bn)原地逆置为(bn,bn-1,…b1,am,am-1,…,a1),再对前n个元素和后m个元素分别使用逆置算法,即可得到(b1,b2,…,bn,a1,a2,…,am),从而实现顺序表的位置互换。

void Reverse(int a[],int left,int right,int arraySize) {
	//先进行全部逆置
	 if (left >= right || right >= arraySize)
        return false;
    int mid=(left+right)/2;
    for (int i=0;i<mid-left;i++){ //不管奇数偶数都可以这样 
    	int temp=A[left+i];
    	A[left+i]= 	A[right-i];
    	A[right-i]=temp;
	} 
}
void Exchange(int A[],int m,int n,int arraySize){
	Reverse (A,0,m+n-1,arraySize);
	Reverse (A,0,n-1,arraySize);
	Reverse (A,n,m+n-1,arraySize);
}

2.2.5 09

线**性表(a1,a2,a3,…,an)中的元素递增有序且按顺序存储于计算机内。要求设计一个算法,完成用最少时间在表中查找数值为x的元素,若找到,则将其与后续元素位置相交换,若找不到,则将其插入表中并使表中元素仍递增有序。

算法思想:折半查找法。

//折半查找法
typedef int ElemType;
void SearchExchangeInsert(ElemType A[],int n, ElemType x){
	int low =0,high=n-1,mid;
	int i;
	//x是要查找的那个数 
	while (low<=high){ 
		mid =(low+high)/2;
		if (A[mid]==x)
			break;
		else if (A[mid]<x) 
			low =mid+1;
		else 
			high=mid-1; 
	}
	//下面的if语句只会执行一个 
	if (A[mid]==x && mid !=n-1) //说明进入了第一个循环并且找到了 
	{
		int t =A[mid];
		A[mid]=A[mid+1];
		A[mid+1]=t; 
		//x就是mid 把x放到后面一个位置 
	}
	//循环结束找不到x的情况
	if(low >high){
		//把x插入在增序的状态
		for(i=n-1;i>high;i--)  //high 与low互换位置 
			A[i+1]=A[i];//后移元素
			A[i+1]=x;  //插入x 
	} 
		
} 

真题部分

10

在这里插入图片描述
在这里插入图片描述

要实现R中序列循环左移P个位置,只需先将R中前P个元素逆置,再将剩下的元素逆置,最后将R中所有的元素再整体做一次逆置操作即可。

//要实现R中序列循环左移P个位置,只需先将R中前P个元素逆置,再将剩下的元素逆置,最后将R中所有的元素再整体做一次逆置操作即可。
void Reverse(int R[],int left ,int right,int arraySize) {
	int t;
	int i;
	//算法的健壮性 
	if (left >=right ||right >=arrraySize ){
		return; 
	}
	for(i = 0; i <= (left + right) / 2; i++) {
		t=R[left+i];
		R[left+i]=R[right-i];
		R[right-i]=t;
	}
}   //可以实现逆置的函数
void Exchange(nt R[],int left ,int right,int arraySize) {
	Reverse(R,0,p-1,arraySize);
	Reverse(R,p,n-1,arraySize);
	Reverse(R,0,n-1,arraySize);
}

上述算法中三个Reverse函数的空间复杂度分别为O(p/2),O((n-p)/2)和O(n/2),故设计的算法的时间复杂度为O(n),空间复杂度为O(1).

在这里插入图片描述

分别求两个升序序列A,B的中位数,设为a和b,求序列A,B的中位数过程如下:

若a=b,则a或b即为所求中位数,算法结束。
若a<b,则舍弃序列A中较小的一半,同时舍弃序列B中较大的一半,要求两次舍弃的长度相等。
若a>b,则舍弃序列A中较大的一半,同时舍弃序列B中较小的一半,要求两次舍弃的长度相等。
在保留的两个升序序列中,重复上述123过程,直到两个序列中只含有一个元素时为止,较小者即为所求的中位数

//上述算法的时间复杂度为O(log2n),采用空间复杂度为O(n)
int M_Search(int A[],int B[],int n){
	//定义变量
	int m1,m2,s1,s2,d1,d2;
	s1=s2=0;
	d1=d2=n-1;
	while(s1!=d1 ||s2!=d2) //当两个数组不为空时 
	{
		//求第一个数组的中位数
		m1=(s1+d1)/2;
        m2=(s2+d2)/2; 
        if (A[m1]==B[m2]){
            return A[m1];
		}
		
        if(A[m1]<B[m2])//A的中位数比B的中位数小
        {
        	//判断一下当前元素是否为奇数
			if((s1+d1)%2==0)//当前比较元素为
            {
                s1=m1;//A的比较范围向后移动,保留中间点
                d2=m2 
            }
			else{//偶数的情况 
                s1=m1+1;//A的比较范围向后移动,不保留中间点
                d2=m2;
            }
		}
		else{ //满足条件三
		
		   if ((s2 + d2) % 2 == 0) {//若元素个数为奇数
                d1 = m1;    //舍弃A中间点以后的部分并保留中间点
                s2 = m2;    //舍弃B中间点以前的部分并保留中间点
            }
            else { //元素个数为偶数
                d1 = m1;    //舍弃A中间点以后部分并保留中间点
                s2 = m2 + 1;//舍弃B中间点及中间点以前的部分
            }

		}
	}
	return A[s1] < B[s2] ? A[s1] : B[s2];
}

12

在这里插入图片描述

//暴力解法构建一个数组
#include "ds.h"

/**
 * 暴力解:空间换时间,开一个新数组,记录每一个数字的出现次数然后判断
 */
int find_main_bf(int A[], int len) {
    int *tmp = (int *) malloc(sizeof(int) * len);
    memset(tmp, 0, sizeof(int) * len);

    for (int i = 0; i < len; i++) {
        tmp[A[i]]++;
        if (tmp[A[i]] > len / 2) {
            return A[i];
        }
    }
    return -1;
}
时间复杂度o(n),空间复杂度O(n)
    
    

13

在这里插入图片描述

在这里插入图片描述

//与上题的暴力解法相似 以空间换时间

 int findMissMin(int A[],int n)
 {
 	int i,*B;  //利用这个指针数组来存出现的数
 	//构建的数组长度为n 
	B=(int *)malloc(sizeof(int)*n);
	memset(B,0,sizeof(int)*n);   //给B指针中赋初值为0 
	for(i=0;i<n;i++){
		if(A[i]>0&&A[i]<=n) //数组里面的值是从1-n记录的 
			B[A[i]-1] =1;    //只要记录出现过就可  
	}
	for(i=0;i<n;i++)
	{
		if(B[i]==0) break;
	} 
	return i+1;
 }
//时间复杂度,A遍历一次,B遍历一次,两次循环内操作步骤为O(1)量级,因此时间复杂度为O(n)。空间复杂度:额外分配了B[n],空间复杂度为O(n)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值