单链表冒泡排序与数组冒泡排序

冒泡排序

冒泡排序是最基本也是最简单的一种排序,但是复杂度高不适合大数据的排序, 但是对于我们学习排序还是很有帮助的。冒泡排序的思想就是每遍历一次就选出最大值或最小值,类似水中的水泡一样,越到水面泡越大。
下面的链表基于这种定义:http://blog.csdn.net/dawn_after_dark/article/details/73610674

形式一

冒泡排序其实有好几种变形,原始的思想就是每一次遍历都要比较相邻两个数的大小,然后交换让大的放到后面,这样每一次遍历最后一个肯定是最大, 外循环为一共需要的遍历的次数,其实n-1次就行了,内循环进行比较交换,a[i]与a[i+1]比较,直到索引i+1小于上一循环中的最大值位置。
比如 8 9 5 4冒泡排序,外循环一共需要3次(4-1)循环
第一次:8与9比较,9大不动,9与5比较,9大,所以9与5交换位置,9与4比较,9大,9与4交换位置,这样选出来最大值放在了最后面。
第二次:8与5比较,8大,8与5交换位置,8与4比较,8大,8与4交换位置,这样8位于倒数第二位置
第三次:5与4比较,5大,5与4交换位置,这样5位于倒数第三个位置。
4自然放在了第一位,后续的已排好序,4就无需与任何比较。
这就是一般冒泡的思想。

数组冒泡排序

    void bubble_sort(int a[], int n){
    int i, j, temp;
    for (j = 0; j < n - 1; j++){
        for (i = 0; i < n - 1 - j; i++){
            if(a[i] > a[i + 1]){
                temp = a[i];
                a[i] = a[i + 1];
                a[i + 1] = temp;
            }
        }
    }

链表冒泡排序

上面的思想运用到单链表中,思路已经在代码中写出。

void LinkList::bubbleSort() {
    if (!head->next) {
        cout << "该链表无节点!" << endl;
        return;
    }
    Node* tail = NULL;   //外循环的结束判断条件,内循环结束的标志指针(tail指向上一轮找出最大值的位置)
    while (tail != head->next->next) { //如果最大值已达到链表第二位置,说明链表就差一个未排序,意思就是排序好了
        Node* pre = head;  //每次外循环开始,先指向头节点
        Node* mid = pre->next;  //指向第一个节点
        while (mid->next != tail) {   //如果下一个节点是上一轮最大值的位置,就没必要再遍历下去,后面肯定很大
            if (mid->value > mid->next->value) { //比较当前节点和相邻的节点的数据大小,若大于则交换
                pre->next = mid->next; //让当前节点的先驱指向相邻的节点
                Node* temp = mid->next->next; //保存相邻节点的后继
                pre->next->next = mid;     //令相邻节点的后继等于当前节点
                mid->next = temp;  //当前节点指向先前保存的后继
                pre = pre->next;   //因为mid交换以后,已经后移了一步,所以这里只需前驱后移
            }
            else {         //如果未交换,就要手动后移这两个指针了
                pre = mid;
                mid = mid->next;
            }
        }
        tail = mid;   //tail指向这一轮找出最大值的位置。
    } 
}

形式二

另一种冒泡排序的思想,从所有的第一个开始,先以第一个为基准,然后与剩余所有的数比较,选出最小的放在第一位位置,之后以第二个位置为基准,与剩余所有的数比较,选出最小的放在第二位置,以此类推,直到全部遍历完。这种思想也是我比较喜欢的,代码看着简单,易理解。

数组冒泡排序

    void bubble_sort(int a[], int n){
    int i, j, temp;
    for (i = 0; i < n - 1; i++){
        for (j = i + 1; j < n ; j++){
            if(a[j] < a[i]){
                temp = a[i];
                a[i] = a[j];
                a[j] = temp;
            }
        }
    }

链表冒泡排序

我们直到在冒泡中要交换数据,所以链表中我通过指针指向的改变,达到交换的目的,而不是简单节点数据交换(这样做没什么意义,如果节点属性过多,这样做无疑是繁琐的)。在链表的反序的实现(http://blog.csdn.net/dawn_after_dark/article/details/73647844)中我们看到了两个节点要想交换位置,必须用2个以上的指针来表明指示要交换节点的前驱和本身,必要时用到后继指针。

void LinkList::bubbleSort() {
    if (!head->next) {
        cout << "该链表无节点!" << endl;
        return;
    }
    Node* pre = head;    //第一个节点的前驱为头节点
    Node* mid = pre->next, *after,*afterPre,*temp;//指向第一个节点,声明内循环中要用的指针
    while (mid) {           //外循环开始
        after = mid->next;  //冒泡第二种形式的思想,让外循环的一个节点与剩余的所有节点一 一比较
        afterPre = mid;     //该节点的前驱指针,方便我们进行交换
        while (after) {     //内循环开始
            if (mid->value > after->value) {  //若节点的值小于外循环中要比较的节点,则交换位置
                afterPre->next = mid;       
                pre->next = after;         //首先让这两个节点的前驱变了
                temp = mid->next;         //保留外循环中要比较的节点的后继,方便交换过来的节点正确的接上
                mid->next = after->next;  //接上交换过来的后继位置
                after->next = temp;      //接上交换过来的后继位置
                temp = mid;       //mid 和 after 所指向的节点在 链表中已交换位置,所以mid和after指针的地址交换一下,思考为什么?
                mid = after;     //
                after = mid;    //答案:因为需要将mid和after的指针的值交换一下,这样才能继续以mid所指向的节点当作外循环的基准节点
                               //after才能正确作为内循环要移动并比较的节点指针,你可以自己敲代码或者画图加深理解
            }
            afterPre = after;   
            after = after->next;  //因为要拿下一个节点与外循环的节点比较,所以要后移前驱和本身
        }
        pre = mid; 
        mid = mid->next;  //本次内循环已选出最小值,所以外循环需要后移一步,继续开始下一轮的内循环遍历
    }
}

你可能感兴趣的:(不刷题心里难受,链表)