王道22数据结构课后习题(第二章2.2.3)

1、从顺序表中删除具有最小值的元素(假设唯一)并由函数返回被删元素的值。空出的位置由最后一个元素填补,若顺序表为空,则显示出错误信息并退出运行。

//删除顺序表L中的最小值元素结点,并通过引用类型参数e返回其值
bool Del_min(SqList &L, int &e)
{
    if(L.length == 0)
        return false;
    int pos = 0
    e = L.data[0];
    for(i = 0 ; i < L.length; i++)
    {
        if(L.data[i] < e)
        {
            e = L.data[i];
            pos = i;
        }
    }
    L.data[pos] = L.data[L.length - 1];
    L.length--;
    return true;
}

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

//将顺序表L的所有元素逆置,空间复杂度为O(1)
void Reverse(SqList &L)
{
    int temp;
    for(i = 0; i < L.length / 2 ; i++)
    {
        temp = L.data[i];
        L.data[i] = L.data[L.length - 1 - i]
        L.data[L.length - 1 - i] = temp;
    }
}

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

void del_x_1(SqList &L, int x)
{
    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;
}

4、从有序顺序表中删除其值在[s,t]之间的所有元素,若s或t不合理或顺序表为空,则显示出错误信息并退出运行。

解法一(有点暴力):

//有序顺序表中删除其值在[s,t]之间的所有元素
bool Del_s_t2(SqList &L, int s, int t)
{
    int k = 0;
    if(s >= t || L.length == 0)
        return false;
    for(int i = 0; i < L.length; i++)
    {
        if((L.data[i] < s) || (L.data[i] > t))
        {
            L.data[k] = L.data[i];
            k++;
        }
    }
    L.length = k;
}

解法二(基于有序的最优解):

bool Del_s_t2(SqList &L, int s, int t)
{
    int i, j;
    if(s >= t || L.length == 0)
        return false;
    for(i = 0; i < L.length && L.data[i] < s; i++);  //寻找大于等于s的第一个元素
    if(i >= L.length)
        return false;
    for(j = i; j < L.length && L.data[j] <= t; j++); //寻找第一个大于t的元素
    for(;j < L.length; i++, j++)
        L.data[i] = L.data[j];
    L.length = i;
    return true;
}

5、从顺序表中删除其值在给定值s与t之间(要求 s < t)的所有元素,若s或t不合理或顺序表为空,则显示出错误信息并退出运行。

也许暴力解就是最优解,和上一题的第一种解法相同:

bool Del_s_t2(SqList &L, int s, int t)
{
    int k = 0;
    if(s >= t || L.length == 0)
        return false;
    for(int i = 0; i < L.length; i++)
    {
        if((L.data[i] < s) || (L.data[i] > t))
        {
            L.data[k] = L.data[i];
            k++;
        }
    }
    L.length = k;
}

6、从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。

//有序表中删除所有值相同的元素(保留一个)
void Delete_Same(SqList &L)
{
    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;
}

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

//将两个有序顺序表合并为一个有序顺序表
bool Merge(SqList A, SqList B, SqList &C)
{
    if(A.length + B.length > C.MaxSize)
        return false;
    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++] = B.data[j++]
    }
    while(i < A.length)
        C.data[k++] = A.data[i++];
    while(j < B.length)
        C.data[k++] = B.data[j++];
    C.length = k;
    return true;
}

8、已知在一维数组A[m+n]中依次存放两个线性表(a1,a2,a3~am)和(b1,b2,b3~bn)。编写一个函数,将数组中两个顺序表的位置互换,即将(b1,b2,b3~bn)放在(a1,a2,a3~an)前面。

垃圾的暴力解:将A[0]与A[m]互换,再将A[1]与A[m+1]互换,一直到将A[m-1]与A[m+n-1]互换,如果此时A[m+n-1]之后还有元素,说明n>m,则把这些元素全部放到A[m]及之后的位置,将已调整的元素往后移。

最优解:将A[m+n]中的元素全部逆置,再将前n个元素与后m个元素分别使用逆置算法。

void Reverse(int A[], int left, int right, int MaxSize)
{
    if(right <= left || right >= MaxSize)
    return false;
    int mid = (right + left) / 2;
    for(int i = 0; i < mid; 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 MaxSize)
{
    Reverse(A, 0, n + m - 1, MaxSize);
    Reverse(A, 0, n - 1, MaxSize);
    Reverse(A, n, m + n - 1,MaxSize);
}

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

void SearchExchangeInsert(SqList &L, int x)
{
    int low = 0, high = n - 1, mid,temp = 0;
    while(low <= high)
    {
        mid = (low + high) / 2;
        if(L.data[mid] == x)
            break;
        if(L.data[mid] < x)
            low = mid + 1;
        else
            high = mid - 1;
    }
    if(L.data[mid] == x && mid != n - 1)
    {
        temp = L.data[mid];
        L.data[mid] = L.data[mid + 1];
        L.data[mid + 1] = temp;
    }
    if(low > high)
    {
        for(i = n-1 ; i > high; i--)
            A[i + 1] = A[i];
        A[i + 1] = x;
    }
}

10、设将n(n>1)个整数存到一维数组R中。设计一个在时间和空间两方面都尽可能高效的算法。将R中保存的序列循环左移p(0<p<n)个位置,即将R中的数据由(X0,X1,~,Xn-1)变换为(Xp,Xp+1,~,Xn-1,X0,X1,~,Xp-1)。要求:

(1)给出算法的基本设计思想。

(2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释。

(3)说明你所设计算法的时间复杂度和空间复杂度。

解法一(暴力解)

/*基本设计思想:用t保存数组首元素,将其余元素向左移动一个单位,
  再将数组首元素加到末尾,即完成了循环左移一位。这样进行p次即完
  成了循环左移p位。*/

//时间复杂度为O(pn)即O(n),空间复杂度为O(1)。

#include <iostream>

using namespace std;

int main()
{
    int R[5] = {1, 2, 3, 4, 5};
    int len = 5, p = 0;
    scanf("%d", &p);
    for(int i = 0; i < p; i++)
    {
        int t = R[0];
        for(int j = 1; j < 5; j++)
            R[j - 1] = R[j];
        R[4] = t;
    }
    for(int i = 0; i < 5; i++)
        printf("%d", R[i]);

}

解法二(最优解):

/*可以将问题是为把数组ab转换为ba(a为前n个元素,b后n-p个元素),
  先将a逆置,再将b逆置,然后对新数组再整体逆置即可。*/
  
/*三个Reverse时间复杂度为O(p/2),O((n-p)/2),O(n/2),故为O(1),
  空间复杂度为O(1)。*/

void Reverse(int R[], int from, int to)
{
    int temp;
    for(int i = from; i < (from + to)/2; i++)
    {
        temp = R[i];
        R[i] = R[to -i];
        R[to - i] = temp;
    }
}

void Converse(int R[], int n, int p)
{
    Reverse(R, 0, p - 1);
    Reverse(R, p, n - 1);
    Reverse(R, 0, n - 1);
    for(int i = 0; i < 5; i++)
    {
        printf("%d", R[i]);
    }
}

11、题目略

编了一下午的次优解:

/*A和B两个序列,用i遍历A,用j遍历B,当A[i]<B[j]时,
  假装将其装入一个升序序列中,用pos表示其中的元素,
  是谁最后时pos变成(L/2)向上取整,就将谁打印出来。*/

/*时间复杂度为O(n),空间复杂度为O(1)*/

#include <iostream>

using namespace std;


int Merge(int A[], int B[])
{
    int pos = 0, j = 0, i = 0;

    while(j < 5 && i < 5)
    {
        if(A[i] > B[j])
        {
            j++;
            pos++;    //记录元素的位序
            if(pos == (10 + 2 - 1)/2)
            {
                printf("%d\n", pos);
                return B[j - 1];
            }
        }
        else
        {
            i++;
            pos++;
            if(pos == (10 + 2 - 1)/2)
            {
                printf("%d\n", pos);
                return A[i - 1];
            }
        }
    }
    while(i < 5)
    {
        pos++;
        if(pos == (10 + 2 - 1)/2)
            {
                printf("%d\n", pos);
                return A[i - 1];
            }
        i++;
    }
    while(j < 5)
    {
        pos++;
        if(pos == (10 + 2 - 1)/2)
            {
                printf("%d\n", pos);
                return B[j - 1];
            }
        j++;
    }
}

int main()
{
    int z;
    int S1[5] = {1, 3, 5, 7, 9};
    int S2[5] = {11, 13, 15, 17, 19};
    z = Merge(S1,S2);
    printf("%d",z);
}

暴力解:

/*暴力解就是额外设置一个数组C用来存放已排好序的队列,
  再求其中位数,因为两个等长序列,故数组C长度为偶数,
  中位数为L/2*/

/*时间复杂度为O(n),空间复杂度为O(n)*/

#include <iostream>

using namespace std;


int Merge(int A[], int B[])
{
    int i = 0, j = 0, k = 0, C[12];
    while(i < 6 && j < 6)
    {
        if(A[i] <= B[j])
            C[k++] = A[i++];
        else
            C[k++] = B[j++];
    }
    while(i < 6)
        C[k++] = A[i++];
    while(j < 6)
        C[k++] = B[j++];
    return C[k / 2];
}

int main()
{
    int z;
    int S1[6] = {1, 3, 5, 7, 9,10};
    int S2[6] = {11, 13, 15, 17, 19, 20};
    z = Merge(S1,S2);
    printf("%d",z);
    return 0;
}

12、题目略

暴力解:

/*暴力解,设置哈希表来存储数组A中各元素出现的次数,
  再用for循环来比较哈希表中各元素出现的次数,
  最后if判断出现次数最多的次数是否大于n/2*/

/*时间复杂度为O(n),空间复杂度为O(n)*/

#include <iostream>

using namespace std;

int nums[100] = {0};

int Findzhu(int A[])
{
    int i,maxx = 0, c = 0;
    for(i = 0; i < 6; i++)
    {
        nums[A[i]]++;
    }
    for(i = 0; i < 6; i++)
    {
        if(A[i] > maxx)
            maxx = A[i];
    }
    for(i = 0; i <= maxx; i++)
    {
        if(c < nums[i])
            c = i;
    }
    if(nums[c] > 6/2)
        return c;
    return -1;
}


int main()
{
    int z;
    int A[6] = {1, 9, 9, 9, 9,5};
    z = Findzhu(A);
    printf("%d",z);
    return 0;
}

13、题目略

暴力解:

/*暴力解*/

/*时间复杂度为O(n^2),空间复杂度为O(n)*/

#include <iostream>

using namespace std;



int FindMissMin(int A[])
{
    int i = 0, j, k = 1;
    while(i < 4)
    {
        if(A[i] < 0)
            i++;
        if(A[i] != k)
            i++;
        else
        {
            i = 0;
            k++;
        }
    }
    return k;
}

int main()
{
    int z;
    int A[4] = {5, -3, 1, 3};
    z = FindMissMin(A);
    printf("%d",z);
    return 0;
}

少暴力一点的解法,先用1和数组元素依次对比,若没有等于1的元素,则返回1,若数组中有1,则把num置0后再用2和数组元素对比..........

int FindMissMin(int A[])
{
    int i, num = 0;
    for(i = 1; ; i++)
    {
        for(int j = 0; j < n; j++)
        {
            if(i == A[j])
                num++;
        }
        if(num == 0)
            return i;  //i就是要找的最小正整数
        else
            num = 0;
     }
}

14、题目略

暴力解(非常暴力):

/*暴力解*/

/*时间复杂度为O(n^3),空间复杂度为O(n)*/

#include <iostream>

using namespace std;


void FindminoTrip(int A[], int B[], int C[])
{
    int i, j, k, d;
    int min_d = abs(A[0] - B[0]) + abs(B[0] - C[0]) + abs(C[0] - A[0]);
    for(i = 0; i < 3; i++)
    {
        for(j = 0; j < 4; j++)
        {
            for(k = 0; k < 5; k++)
            {
                d = abs(A[i] - B[j]) + abs(B[j] - C[k]) + abs(C[k] - A[i]);
                if(d < min_d)
                    min_d = d;
            }
        }
    }
    printf("最小距离为%d\n", min_d);
    printf("三元组为:",A[i], B[j], C[k]);
    for(i = 0; i < 3; i++)
    {
        for(j = 0; j < 4; j++)
        {
            for(k = 0; k < 5; k++)
            {
                if(min_d == abs(A[i] - B[j]) + abs(B[j] - C[k]) + abs(C[k] - A[i]))
                    printf("(%d,%d,%d)\n",A[i], B[j], C[k]);
            }
        }
    }
}

int main()
{
    int S1[3] = {-1, 0, 9};
    int S2[4] = {-25, -10, 10, 11};
    int S3[5] = {2, 9, 17, 30, 41};
    FindminoTrip(S1, S2, S3);
    return 0;
}

  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值