数据结构(一)线性表练习题1

线性表的顺序表示

  1. 将顺序表所有元素逆置,要求空间复杂度O(1)
    将前一半和后一半交换
void reverse(SqList &L){
     Elemtype temp;
     for(int i=0;i<L.length/2;i++){
     temp = L.data[i] ;
     L.data[i] = L.data[L.length-i-1];
     L.data[L.length-i-1] = temp;
     }
}
  1. 长度为n的顺序表L,时间复杂度O(N),空间复杂度O(1),删除其中所有值为x的元素
    解法1:使用k记录顺序表中等于x的元素个数,边扫描边统计k,并将不等于x的向前移动K个位置,并修改length的长度。
LinkList deleteElem(LinkList &L,Elemtype x){
if(L == NULL)
   return NULL;
int k=0;
for(int i=0;i<L.length;i++)
   if(L.data[i] == x){
       k++;
   }
   else{
   L.data[i-k] = L.data[i];//每次只移动当前元素
   }
}
L.length =  L.length - k;
return L;
}

解法2:记录不等于x的元素个数k,每次只移动一个元素,只将当前元素赋值到k的位置上

LinkList deleteElem(LinkList &L,Elemtype x){
if(L == NULL)
   return NULL;
int k=0;
for(int i=0;i<L.length;i++)
{
   if(L.data[i]!=x)
   {
      L.data[k] = L.data[i];
      k++;
   }
}
L.length = k;
return L;
}

前两种解法都是每次只移动当前的1个元素而不是移动当前元素后面的所有元素,保证时间复杂度O(n),遍历是无法改变的,所以考虑改变遍历里面的操作。
解法3:设置头尾两个指针同时向中间移动,检测到最左侧的为x的元素,将其与最右侧的非x元素进行交换,但是会改变元素的相对位置

  1. 有序顺序表中删除其值在s和t之间的所有元素,如果s或t不合理或者顺序表为空显示出错信息
    找到第一个大于s的元素,然后找到第一个小于t的元素,将后面的所有元素前移然后更新数组长度。
//两步操作写在一起
LinkList delete1(LinkList &L,ElemType s,ElemType t){
     if(L == NULL || s>t)
        return NULL;
     int i=0;
     int k = 0;
     while(i<L.length){
      if(L.data[i] > s && L.data[i] < t)
        k++;
      else
       L.data[i-k] = L.data[i];
       i++;
     }
     L.length = L.length - k;
    return L;
}
//两步操作分开
LinkList delete2(LinkList &L,ElemType s,ElemType t){
     if(L == NULL || s>t)
        return NULL;
     int i,j;
     for(i=0;i<L.length&&L.data[i]<s;i++);
     //在这里加一个判断i是否超出数组的长度更好
     if(i>=L.length)
        return NULL;
     for(j=i;j<L.length&&L.data[j]<t;j++);
     while(j<L.length){
     L.data[i] = L.data[j];
     i++;
     j++;
     }
     L.length = i+1;//最后的元素位置是i代表的,但是长度和游标有+1的区别
     return L;
}        
  1. 从顺序表中删除删除其值在s和t之间的所有元素,如果s或t不合理或者顺序表为空显示出错信息(这里可以用问题3的第一种方法
LinkList delete(LinkList &L,ElemType s,ElemType t){
     if(L == NULL || s>t)
        return NULL;
     int i=0;
     int k = 0;
     while(i<L.length){
      if(L.data[i] > s && L.data[i] < t)
        k++;
      else
       L.data[i-k] = L.data[i];
       i++;
     }
     L.length = L.length - k;
    return L;
}

  1. 有序顺序表中删除所有其值重复的元素,使表中所有元素不同
    利用直接插入排序的思想,从头到尾扫描数组,将从第一个开始的部分看成是一个非重复数组,然后将后面的不断插入,最后的非重复数组的长度直接赋值给length。这里的插入不需要将后面的所有元素前移,直接给非重复数组当前指针指向位置赋值即可,只要有重复必定出现至少两次,不会出现掩盖还未处理的非重复数。
LinkList delete(LinkList &L){
   if(L ==NULL)
   return NULL;
   int i,j;
   for(i=0,j=1;j<L.length;j++{
       if(L.data[i]!=L.data[j])
         L.data[++i]=L.data[j];
   }
   L.length = i+1;//虽然前面是++i才赋值,但是最后退出的时候是加完之后的被赋值,所以最后并没有多出一个空的,所以i还是要+1
   return L;
}
  1. 如果是无序顺序表,O(n)的算法
    思路一:O(N^2)的有一个比直接后面覆盖前面的要好一些的方法就是内层循环换成从前向当前节点移动的循环
    思路二:使用hash表
    思路三:排序+删除(下面没有代码)
//如果是两层for循环可以处理当前节点前面的做内层循环,也可以遍历后面的节点做内层循环,时间复杂度O(N)
LinkList delete1(LinkList &L){
    int i,j;
    for(i=1;i<L.length;i++){
      j=0;
      while(j<i){
        if(L.data[i]==L.data[j])
             L.data[j]=L.data[i];
         j++;
        }   
      }  
}
//如果从后往前推的话只能移动所有元素,不能直接进行一步赋值,因为不能保证后面重复元素出现的次数
//第一种内层循环是从第一个到当前节点的可以保证重复节点的次数不超过2次,所以可以通过一次赋值进行改变
LinkList delete2(LinkList &L){
   int i,j;
   for(i=0;i<L.length;i++)
      for(j=i+1;j<L.length;j++)
         if(L.data[i]==L.data[j])
           over
}
//使用hash函数,空间复杂度o(N),时间复杂度O(n),使用空间换取时间
# define Maxsize 10000
LinkList delete3(LinkList &L){
   int index[Maxsize];
   for(int i=0;i<Maxsize;i++)
     index[i] = 0;
   for(int i=0;i<L.length;i++)
   if(index[L.data[i]]!=0)
       over//当前节点可以被删除,但是具体删除的操作怎么进行呢?
}
LinkList delete30(LinkList &L){
     int index[Maxsize];
    for(int i=0;i<Maxsize;i++)
     index[i] = 0;
    for(int i=0;i<L.length;i++)
      index[L.data[i]]++;
    int k =0;
    for(int i=0;i<Maxsize;i++)
      if(index[i]!=0)
         L.data[k]=i;
    L.length = k;
    return L; 
}
  1. 将两个有序顺序表合并成一个有序顺序表
LinkList merge(LinkList &L1,LinkList &L2){
  int i,j;
  i=0;
  j=0;
  LinkList L3 = NULL;
  L3.length = L2.length+L1.length;
  int k=0;
  while(i<L1.length&&j<L2.length)
  {
     if(L1.data[i]<=L2.data[j])
         L3.data[k]=L1.data[i];
     else
         L3.data[k]=L2.data[j];
     k++;
}
  while(i<L1.length){
     L3.data[k]=L1.data[i];
     k++;
  }
  while(j<L2.length){
    L3.data[k]=L2.data[i];
     k++;
  }
  return L3;
}
  1. 已知在一维数组A[m+n]中存储着两个线性表,写一个函数将数组中的两个顺序表位置调换
    思路一:直接将前面的m长度的数组向后移动,后面的n数组赋值到从第一个往后的位置
    思路二:利用数组逆序,首先逆序整个数组,然后前面的m个和后面的n个分别逆序
  //如果令开辟一个空间的话直接赋值即可
  //如果不开辟空间可以使用直接插入排序的方法
  //时间复杂度O(nm)
  LinkList reverse1(LinkList &L,int m,int n){
    int i,j,k=0;
    for(i=m;i<m+n-1;i++)
   {int temp = L.data[i];
    for(j=m+k-1;j>=k;j--){
         L.data[j+1]=L.data[j];
      }
      L.data[k] = temp;
      k++;
}   
 return L;
}
//首先将全部元素逆序,然后分别逆序前m个和后n个
void verse(LinkList &L,int start,int end){
int temp;
int middle = (end-start)/2;
for(int i=start;i<middle;i++)
{
  temp = L.data[i];
  L.data[i] = L.data[i+middle];
  L.data[i+middle]=temp;
}
}
LinkList reverse2(LinkList &L,int m,int n){
  verse(L,0,m+n);
  verse(L,0,m);
  verse(L,m,m+n);
  return L;
}
  1. 线性表中元素递增有序,要求设计一个算法可以在最少时间在表中查找到值为x的元素,找到后将其与后继元素位置进行交换,找不到将其插入到线性表中但是仍保证递增有序。
    注意:这里遗漏了如果匹配上的x是最后一个元素,则不存在与后继元素交换位置的情况
LinkList findChange(LinkList &L,ElemType x){
 int left=0;
 int right=L.length-1;
 int middle = (right-left)/2;
 while(left<right){
  if(L.data[middle]==x&&middle!=L.length-1)//这里注意判断条件还要包括不是最后一个元素匹配上
    {
    int temp = L.data[middle];
    L.data[middle]=L.data[middle+1];
    L.data[middle+1]=temp;
}
  else if(L.data[middle]<x)
  {
      right = middle;
  }
  else{
      left = middle+1;
}
//没有找到进行插入,直接插入到left后面就行
for(int i = L.length-1;i>left ;i++)
{
    L.data[i+1]=L.data[i]; 
}
L.data[left+1]=x;
}
return L;
}
  1. 将n个整数存放在一维数组R中,设计一个在时间和空间上都高效的算法。将R中保存的序列循环左移p(0<p<1)个位置。
    思路:和前面的m+n逆序是一样的思路,同理可知一个数组的循环位置改变可以使用两次逆序的方法
    同样也可以适用辅助数组来解,先将前p个暂存在辅助数组中,然后将后面的向左移动,然后将暂存的数组重新复制回原数组。[时间复杂度O(n),空间复杂度O§]
void verse(LinkList &L,int start,int end){
int temp;
int middle = (end-start)/2;
for(int i=start;i<middle;i++)
{
  temp = L.data[i];
  L.data[i] = L.data[i+middle];
  L.data[i+middle]=temp;
}
}
void reverse(LinkList &L,int p){
   verse(L,0,L.length);
   verse(L,0,L.length-p);
   verse(L,L.length-p,L.length);
}
  1. 现在有两个等长升序序列,设计一个在时间和空间上都高效的算法,找出两个序列A和B的中位数。(这个不是绝对数学上的中位数,这里有限制是n/2取天棚运算。
int findMiddle(LinkList &L1,LinkList &L2){
    int left1,left2;
    left1=left2=0;
    int right1,right2;
    right1 = L1.length;
    right2 = L2.length;
    while(left1<=right1&&left2<=right2){
    int middle1=(right1-left1)/2;
    int middle2=(right2-left2)/2;
    int middle = (L1.data[middle1]+L2.data[middle2])/2;
    if(L1.data[middle1]==middle&&L2.data[middle2]==middle)
        return middle;
    if(L1.data[middle1]<middle)
         left1 =  middle1+1;
    else{
        right1 = middle1;}
    if(L2.data[middle2]<middle)
        left2 = middle1+1;
    else{
        right2 = middle2;
        }   
}
    return middle;
}

思路:从二者的中位数直接判断
共分3种情况:
a、如果数组1的中位数和数组2相等,直接返回
b、如果数组1的中位数小于数组2的中位数,除去数组1左半部分;除去数组2右半部分
c、如果数组1的中位数大于数组2的中位数,除去数组2左半部分,除去数组1右半部分
然后继续比较剩下的部分的两个数组的中位数大小
注意:这里涉及到求中位数的时候数组元素个数是奇数还是偶数的问题

int search(int A[],int B[],int n){
  int s1=0,d1=n-1,m1,s2=0;d2=n-1,m2;
  while(s1!=d1&&s2!=d2){
      //m1 = (d1-s1)/2;
     // m2 = (d2-s2)/2;
     m1 = (d1+s1)/2;//这里要使用+,在后半部分的时候求middle的下标就是+才能得到
     m2 = (d2+s2)/2;
     if(A[m1]==B[m2])
        return A[m1];
     else if(A[m1]<B[m2])
     {
        if((s1+d1)%==0)//如果除完说明是偶数,则证明原来的数组是奇数,也就是中间的数直接可以代表中位数,所以这里保留中间的数
         {
         s1 = m1;
         d2 = m2;
}
else{//说明原数是偶数,所以不保留中间数
     s1 = m1+1;
     d2 = m2;
}  
     }
     else{
      if((s2+d2)%2==0){
      s2=m2;
      d1 = m1;
}else{
      s2 = m2+1;
      d1 = m1;
}
    
}
//直到两个数列中均只有一个元素的时候取较小的元素为中位数
return A[s1]<B[s2]?A[s1]:B[s2];
}

❀13. 已知一个整数序列A=(a0,a2,…,an-1),其中0<=ai<n。如果在该序列中存在一个整数满足该整数在序列中出现的次数大于n/2,则该整数为主元素,试找出该整数序列中的主元素,如果存在输出,否则输出-1
思路:设置一个Number存储首先选定的可能为主元素的元素,然后设置一个count存储这个元素的出现次数,count更新方式是:如果下一个元素与当前的Number相等则count++,否则count–;如果count更新到0,则换下一个数进行计数,直到扫描完整个数组,然后判断当前的count是否大于n/2 ,从头遍历整个数组重新统计当前count所对应的Number是否是主元素,开始的count只能保证当前的Number是出现次数最多的元素,但是这个元素出现的次数是否大于n/2还是需要重新判断,因为在不等于Number的时候将count–了(这里放到扫描完整个数组再进行判断,最后主元素只能有一个,最后判断一次即可以决定整个数组是否含有主元素)

int findMax(int A[],int n){
int k = 0;
int Number = A[k];
int count = 0;
while(k<n-1){
for(int i = 0;i<n;i++){
   if(A[i] == Number)
     count++;
   else{
    if(--count==0)
       Number = A[++k];
    }
  }
}
for(int i=0;i<n;i++)
 if(A[i]==Number)
   count++; 
if(count > n/2)
  return Number;
else
  return -1;
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值