2024 王道数据结构 代码习题部分 第八章 排序

第八章 排序

8.3交换排序

2.编写双向冒泡排序算法,在正反两个方向交替进行扫描,即第一趟把关键字最大的元素放在序列的最后面,第二趟把关键字最小的元素放在最前面,如此反复进行。

void BubbleSort(ElemType A[],int n){

    int low = 0;

    int high = n-1;

    bool flag = true;

    while(low<high && flag){

        flag = false;

        for(i=low;i<high;i++){

            if(A[i]>A[i+1]){

                swap(A[i],A[i+1]);

                flag = true;

            }

        }

        high--;

        for(i=high;i>low;i--){

            if(A[i]>A[i-1]){

                swap(A[i],A[i-1]);

                flag = true;

            }

        }

        low++;

    }

}

3.已知线性表按顺序存储,且每个元素都是不相同的整数型元素,设计把所有奇数移动到所有偶数前的算法(要求时间最少,辅助空间最少)。

// 思路:采用快速排序的划分思想。先从前往后找到一个偶数元素L(i),再从后往前找到一个奇数元素L(j),将二者交换,直至i大于j。

void move(ElemType A[],int len){  // 1 2 5 8        1 2 5 8 11

    int low=0,high=len-1;

    while(low<high){

        while(low<high && A[low]%2!=0)  // 找一个偶数

            low++;

        while(low<high && A[high]%2!=1) // 找一个奇数

            high--;

        if(low<high)

            swap(A[low],A[high]);

        low++;

        high--;

    }

}

4.重新编写快速排序的划分算法,使得每次选取的的枢轴值都是从当前子表中随机选择的。

void Partition2(ElemType A[],int low,int high){

    int rand_index = low + rand()%(high-low+1);

    swap(A[rand_index],A[low]);

    ElemType pivot = A[low];    // 选取枢轴

    int i = low;                    // low X ··· high

    for(int j=low+1;j<=high;j++)    //  i  j

        if(A[j]<pivot)

            swap(A[++i],A[j]);

    swap(A[i],A[low]);              // 最终使得A[low ··· i]中的所有元素都小于pivot

    return i;   // 返回基准元素位置

}// 核心思想:j从子表的第二个元素开始遍历,如果这个元素小于枢轴,就让它交换到前面来(可以认为从第二个元素开始,初始是一个空表)

// 5 || 1 7 11 4 9 8

// 5 || 1 4 11 7 9 8

//        i

// 4    1 5 11 7 9 8

5.试编写一个算法,使之能够在数组L[1...n]中找出第k小的元素(即从小到大排序后处于第k个位置的元素)。

// 思路:最直接的办法是直接进行排序(快排、堆排序(小根堆)),O(nlog2n) O(n+klog2n)。

// 巧妙办法:选取枢轴,进行类似于快速排序的划分操作。L[1...n]被划分成L[1...m-1]和L[m+1...n]   L(m)=pivot

// 相当于划分后,确定了所选择枢轴的最终位置。

// 1) m=k    直接返回pivot

// 2) m<k    所找元素落在右子表,对L[m+1,n]递归查找第k-m小的元素。(左子表中的m-1个元素和枢轴都小于目标元素)

// 3) m>k    所找元素落在左子表,对L[1,m-1]递归查找第k小元素

// 时间复杂度O(n)    空间复杂度取决于具体情况

void Find_KMin(ElemType A[],int low,int high,int k){

    int pivot = A[low];

    int low_temp = low;     // 暂存,后续递归时要使用

    int high_temp = high;

    while(low<high){

        while(low<high && A[high]>=pivot)

            --high;

        A[low] = A[high];

        while(low<high && A[low]<=pivot)

            ++low;

        A[high] = A[low];

    }

    A[low] = pivot;     // 枢轴元素就位

    if(low==k)

        return A[low];

    else if(low<k)

        Find_KMin(A,low+1,high_temp,k-low);     // 在右子表中找第k-low小的元素

    else

        Find_KMin(A,low_temp,low-1,k);  // 在左子表中找第k小的元素

}

6.荷兰国旗问题:设有一个仅由红、白、蓝三种颜色条块组成的条块序列,请编写一个时间复杂度为O(n)的算法,使得这些条块按照红白蓝的顺序排好,即排列成荷兰国旗的图案。

// 思路:将红色条块交换到线性表的最前面,蓝色条块交换到线性表的最后面。

// 使用三个游标,i,r,b     i遍历数组,r之前的条块为红色,b之后的条块都为蓝色      r初始为0,b初始为n-1

typedef enum{RED,WHITE,BLUE} color;     // 设置枚举数组

void Flag_Arrange(color a[],int n){

    int i = 0;

    int r = 0;

    int b = n-1;

    while(i<=b){

        switch(A[i]){

        case RED:               // 红 白 红 白

            Swap(A[i],A[r]);    //    r i

            i++;

            r++;

            break;

        case WHITE:

            i++;

            break;

        case BLUE:

            Swap(A[i],A[b]);    //  白 蓝 红 白 蓝

            b--;                //     i       b

            break;      // 此处i不能++,以防出现交换后A[i]仍未蓝色的情况

        }

    }

}

// 思考:为什么红色不需要考虑交换后仍然为红色的问题?

// 因为,i是从0开始遍历的,如果扫描到红色,会自己和自己进行交换,r会++,r所指的位置不可能是红色。参见上方示例。而b所指的位置并没有被遍历处理过。

7.[2016]已知由n(n>=2)个正整数构成的集合A={ak|0<=k<n},将其划分为两个不相交的子集A1和A2,元素个数分别是n1和n2,A1和A2中的元素之和分别为S1和S2。设计一个尽可能高效的划分算法,满足|n1-n2|最小且|S1-S2|最大。

// 思路:将最小的 n/2(向下取整)个元素放在A1中,其余元素放在A2中。 无需对所有元素进行全部排序。

// 仿照快速排序,基于枢轴划分成两个子集。

// 1) i=n/2(向下取整)   分组完成

// 2) i<n/2(向下取整)   枢轴及枢轴之前的元素均属于A1,对枢轴之后进行再次划分

// 3) i>n/2(向下取整)   枢轴及枢轴之后的元素均属于A2,对枢轴之前进行再次划分

void SetPartition(int A[],int n){

    int pivot;

    int low = 0, high = n-1;

    int low0 = 0, high0 = n-1;

    int k = n/2;

    bool flag = true;

    while(flag){

        pivot = A[low];

        while(low<high){

            while(low<high && A[high]>=pivot)

                --high;

            if(low!=high)   // 此处判断多余???

                A[low] = A[high];

            while(low<high && A[low]<=pivot)

                ++low;

            if(low!=high)

                A[high] = A[low];

        }

        A[low] = pivot;

        if(low==k-1)   // 枢轴是第n/2小元素,划分成功。(数组下标相差1)

            flag = false;

        else if(low<k-1){   // 对枢轴之后进行再次划分 0 1 ··· low ········· n-1

            high = high0;

            low0 = ++low;   // 暂存,以便后续使用

        }else{              // 对枢轴之前进行再次划分 0 1 ········· low ··· n-1

            low = low0;

            high0 = --high; // 暂存,以便后续使用

        }

    }

}

8.4 选择排序

4.编写一个算法,在基于单链表表示的待排序关键字序列上进行简单选择排序。

// 思路:不断摘下待排序序列中关键字最大的结点,将其插入链表的最前端。

void SelectSort(LinkList &L){   // 不带头结点

    LinkNode *head = L;     // 待排序链表的头结点

    L = NULL;

    LNode *max;     // 暂存最大结点

    LNode *maxpre;  // 暂存最大结点的前驱

    LNode *r;   // 游标

    LNode *pre; // 游标前驱

    while(head!=NULL){

        r = head;

        pre = NULL;

        max = head;     // 假设待排序链表的头结点是最大的

        maxpre = NULL;

        while(r!=NULL){

            if(r->data > max->data){

                max = r;

                maxpre = pre;

            }

            pre = r;    // 游标、游标前驱后移

            r = r->next;

        }

        if(max==head)       // 最大结点是头结点 --> 头结点摘走,头指针要后移,不然断链了

            head = head->next;

        else

            maxpre->next = max->next;

        max->next = L;  // 前插至结果链表

        L = max;        // 结果链表头指针移动

    }

}

5.设计一个算法,判断一个数据序列是否构成一个小根堆。

// 思路:将顺序表看作是一个完全二叉树,扫描所有分支结点。若孩子结点的关键字小于根结点的关键字,返回false。

bool IsMinHeap(ElemType A[],int len){

    if(len%2==0){ // 存在一个单分支结点

        if(A[len/2]>A[len]) // len/2 ---> 最后一个分支结点      (len/2)*2=len  ---->  最后一个分支结点的左孩子

            return false;

        for(i=len/2-1;i>=1;i--) // 判断所有双分支结点

            if(A[i]>A[2*i] || A[i]>A[2*i+1])  // 如果根结点>左孩子 或者 根结点>右孩子

                return false;

    }else{  // 没有单分支结点

        for(i=len/2;i>=1;i--){

            if(A[i]>A[2*i] || A[i]>A[2*i+1])

                return false;

        }

    }

}

(三)8.6 各种内部排序算法的比较及应用

02.设顺序表用数组A[]表示,表中元素存储在数组下标1~m+n的范围内,前m个元素递增有序,后n个元素递增有序,设计一个算法,使得整个顺序表有序。

// 思路:将A视为已经进行m趟插入排序的表,将后面的n个元素视为待排序元素插入到前面的有序表中。

void Sort(int A[],int m,int n){

    for(int i=m+1;i<=m+n;i++){

        if(A[i]<A[i-1])

            A[0] = A[i];

        for(int j=i-1;A[j]>A[0];j--)

            A[j+1] = A[j];

        A[j+1] = A[0];

    }

}

// 也可以使用归并排序

void Merge(int A[],int m,int n){

    int *B = (int *)malloc(sizeof(int)*(m+n));

    int i;

    for(i=0;i<=m+n;i++)

        B[i] = A[i];

    int low = 1;

    int high = m+1;

    i = 1;

    while(low<=m && high<=m+n){

        if(B[low]<B[high])

            A[i++] = B[low++];

        else

            A[i++] = B[high++];

    }

    while(low<=m)

        A[i++] = B[low++];

    while(high<=m+n)

        A[i++] = B[high++];

}

03.有一种简单的排序算法,称为计数排序(count sorting)。(这种排序算法对一个待排序的表(用数组表示)进行排序,并将排序结果存放到另一个新的表中。必须注意的是,表中所有待排序的关键码互不相同,计数排序算法针对表中的每个记录,扫描待排序的表一趟,统计表中有多少个记录的关键码比该记录的关键码小,假设针对某个记录统计出的计数值为c,则这个记录在新有序表中的合适存放位置即为c。

// 1)设计实现计数排序的算法。

// 2)对于有n个记录的表,关键码比较次数是多少?

// 3)与简单选择排序相比较,这种方法是否更好?为什么?

ElemType* CountSort(ElemType A[],int n){

    ElemType *B = (ElemType *)malloc(sizeof(ElemType)*n);

    int count = 0;

    int i,j;

    for(i=0;i<n;i++){

        for(j=0;j<n;j++)

            if(A[j]<A[i])

                count++;

        B[cout] = A[i];

    }

    return B;

}

//(2)对于有n个记录的表,每个关键码都要与n个记录含自身进行比较,比较次数n^2。

//(3)简单选择排序好。简单选择排序比较次数n(n-1)/2,只使用一个位置用来交换记录。此方法比较次数n^2,且需要另一组数组空间。

// 对题目进行修改:任意元素之间只允许对比一次。

ElemType* CountSort(ElemType A[],int n){

    // 比较部分的代码

    int *Count = (int *)malloc(sizeof(int)*n);

    int i,j;

    for(i=0;i<n;i++)

        Count[i] = 0;

    for(i=0;i<n;i++){

        for(j=i+1;j<n;j++)

            if(A[j]<A[i])

                Count[i]++;

            else

                Count[j]++;

    }

}

04.设有一个数组中存放了一个无序的关键序列K1,K2,…,Kn。现要求将Kn放在将元素排序后的正确位置上,试编写实现该功能的算法,要求比较关键字的次数不超过n。

int Partition(ElemType K[],int n){

    int low = 0;

    int high = n-1;

    ElemType pivot = K[n-1];

    while(low<high){

        while(low<high && K[low]<=pivot)

            low++;

        if(low<high)

            K[high] = K[low];   // 移动到右端

        while(low<high && K[high]>=pivot)

            high--;

        if(low<high)

            K[low] = K[high];   // 移动到左端

    }

    K[low] = pivot;

    retrun low;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
王道数据结构课后习题中,有一个关于排序的题目。根据引用的思路,我们可以使用排序后查找的方法来解决这个问题。具体步骤如下: 1. 首先,对给定的数据进行排序,可以使用快速排序、归并排序等常见的排序算法进行排序排序的时间复杂度为O(log2n)。 2. 排序完成后,我们可以使用二分查找的方法来查找特定元素。如果目标元素存在于排序后的数组中,可以通过遍历判断其是否存在。时间复杂度为O(log2n)。 所以,根据引用的思路,我们可以使用排序后查找的方法来解决王道数据结构课后习题排序的问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [leetcode答案-kaoyan-data-structure:21考研,用的去年的书《2020王道数据结构》](https://download.csdn.net/download/weixin_38733733/19920375)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [王道数据结构课后题编程(顺序表)](https://blog.csdn.net/RHJlife/article/details/90450834)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值