• 合并两个排好序的链表


    接下来要学习跟排序相关的算法了,在正式之前先来个小热身,将两个“已排好序”的链表进行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)

  • 相关阅读:
    Java线程的学习(一)——以售票系统为例
    web笔记
    ssm2之applicationContext.xml文件配置
    ssm笔记1
    在ViewHolder中添加item点击事件接口(自定义
    Java反射机制
    新手导航页(小圆点
    jsoup
    TextView设置随机大小和颜色
    常用IO流
  • 原文地址:https://www.cnblogs.com/webor2006/p/7105852.html
Copyright © 2020-2023  润新知