合并两个排好序的链表

接下来要学习跟排序相关的算法了,在正式之前先来个小热身,将两个“已排好序”的链表进行merge操作,先编写一个代码框架,这个比较简单不过多解释:

#include <iostream>

struct node{
    int payload;
    node* next;
    node(int payload) {
        this->payload = payload;
        next = nullptr;
    }
};

class linkedlist
{
    node *head, *tail;
public:
    linkedlist() : head(nullptr), tail(nullptr) {

    }
    //从尾部插入元素
    void push_back(int value) {
        //TODO
    }

    //输出链表元素
    void output() {
        //TODO
    }
    
};

linkedlist merge(linkedlist a, linkedlist b) {
    //TODO
    return a;
}

int main(void) {

    linkedlist a, b;
    //按顺序插入数据
    a.push_back(4);  a.push_back(7);  a.push_back(20);
    b.push_back(2);  b.push_back(3);  b.push_back(9);  b.push_back(29);

    linkedlist result = merge(a, b);
      result.output();
    return 0;
}

接着来完善linkedlist类中的方法,对于一个链表通常会有:插入数据、取出数据、弹出数据、判断当前链表是否为空、输出整个链表,下面一一来完善:

#include <iostream>

struct node{
    int payload;
    node* next;
    node(int payload) {
        this->payload = payload;
        next = nullptr;
    }
};

class linkedlist
{
    node *head, *tail;
public:
    linkedlist() : head(nullptr), tail(nullptr) {

    }
    //从尾部插入元素
    void push_back(int value) {
        if(empty()) {
            //如果是空链表,则头尾节点都指向新插入的
            head = tail = new node(value);
        } else {
            tail->next = new node(value);
            tail = tail->next;
        }
    }

    //获得头一个元素的值
    int front() {
        if(empty()){
              throw "The list is empty.";
        }
        return head->payload;
    }

    //弹出头节点
    void pop_front() {
        if(empty()){
              throw "The list is empty.";
        }
        node* first_node = head;
        head = head->next;
        delete first_node;
    }

    bool empty() {
        return head == nullptr;
    }

    //输出链表元素
    void output() {
        node* iterator = head;
        while(iterator) {
            std::cout << iterator->payload << " ";
            iterator = iterator->next;
        }
        std::cout << std::endl;
    }
    
};

linkedlist merge(linkedlist a, linkedlist b) {
    //TODO
    return a;
}

int main(void) {

    linkedlist a, b;
    //按顺序插入数据
    a.push_back(4);  a.push_back(7);  a.push_back(20);
    b.push_back(2);  b.push_back(3);  b.push_back(9);  b.push_back(29);

    linkedlist result = merge(a, b);
      result.output();
    return 0;
}

接下来则是到了如何合并两个链表的关键实现了,如何实现呢?

分以下几个条件来进行思路整理:

1、边界循环条件是两个要合并的链表中不能都为空。

2、如果两个链表中有一个为空链表,那这合并就比较简单了,直接把非空的链表中的元素一个个加到新链表中,然后将非空的链表pop_front()。

3、如果两个链表都没有一个为空的情况,那就是合并算法最核心的啦,其做法也是比较简单的,如下图所示:

这样结构就变成了:

接着再进行同样的比较,如下:

此时结构就变成了:

以此循环,直到会遇到有个链表为空的情况,如下:

那接下来再次循环,就会走到如下条件了,那就直接将链表b的数据拿到result中既可,如下:

下面将merge方法完善一下:

编译运行:

那这个合并算法的时间复杂度是多少呢?貌似不是特别好算,因为循环条件是一个纯逻辑的东东,看不出具体循环了多少次,这里可以观察一下条件判断,每次条件都会要pop_front(),如下:

假设链表a的长度是m、链表b的长度是n的话,总共循环应该是(m + n)次,而方法中其它的一些语句都是常量级别的,跟元素个数木有关系:

所以整个merge的时间复杂度是:(m + n) * O(1) = O(m + n)

原文地址:https://www.cnblogs.com/webor2006/p/7105852.html