剑指Offer - 面试题25:合并俩个排序的链表

Posted 林夕07

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指Offer - 面试题25:合并俩个排序的链表相关的知识,希望对你有一定的参考价值。

题目

输入俩个递增排序的链表,合并这俩个链表并使新链表中的节点仍然是递增序列。例如下图链表1和链表2,合并后的升序链表为链表3,链表节点定义如下:

typedef int TElemType;//链表节点值的数据类型
struct ListNode
{
    TElemType m_nValue;
    ListNode* m_pNext;
};

在这里插入图片描述

分析

我们可以构造出来一个链表。表头不存储数据,定义俩指针p1p2分别指向链表1和链表2表头。再创建一个记录新的链表尾部的指针previously。然后对比俩指针所指节点的值,谁小取谁的值,然后对应的指针后移。将新的节点连接到previously指向的节点后面,然后更新previously。直到俩个链表有一个为nullptr或者俩个均为nullptr
当有一个链表为空退出循环时,直接将previously的下一个节点连接到另一个链表指针的节点。若均为nullptr时,不进行任何操作。(但是如果题目要求不允许修改原链表,你就只能继续遍历链表,然后创建新节点,连接。)

下面代码是不允许修改的原链表代码,且未优化
C++

#include <iostream>
using namespace std;

typedef int TElemType;//链表节点值的数据类型
struct ListNode
{
    TElemType m_nValue;
    ListNode* m_pNext;
};

ListNode* CreateNode(TElemType val)//创建节点
{
    ListNode* Node = new ListNode();
    Node->m_nValue = val;
    Node->m_pNext = nullptr;
    return Node;
}

void connect(ListNode* L1, ListNode* L2)//链接
{
    L1->m_pNext = L2;
}

void PrintList(ListNode* head)
{
    while (head != nullptr)
    {
        cout << head->m_nValue;
        if (head->m_pNext != nullptr)
        {
            cout << "->";
        }
        else
        {
            cout << endl;
        }
        head = head->m_pNext;
    }
}

ListNode* Merge(ListNode* pHead1, ListNode* pHead2)//合并俩链表
{
    if (nullptr == pHead1 && nullptr == pHead2)
    {
       return nullptr;
    }

    if (nullptr == pHead1)
    {
        return pHead2;
    }

    if (nullptr == pHead2)
    {
        return pHead1;
    }

    ListNode* p1 = pHead1;
    ListNode* p2 = pHead2;
    ListNode* NewNode = nullptr;//新创建的节点指针

    ListNode* head = new ListNode();//新链表的表头指针
    head->m_nValue = 0;
    head->m_pNext = nullptr;
    ListNode* previously = head;//表尾指针

    
    while (p1 != nullptr && p2 != nullptr)
    {
        int min = 0;//用于保存小值的节点

        //找最小值
        if (p1->m_nValue < p2->m_nValue)
        {
            min = p1->m_nValue;
            p1 = p1->m_pNext;
        }
        else
        {
            min = p2->m_nValue;
            p2 = p2->m_pNext;
        }

        //创建新节点
        NewNode = new ListNode();
        NewNode->m_nValue = min;
        NewNode->m_pNext = nullptr;

        //连接
        previously->m_pNext = NewNode;

        //更新链表尾指针
        previously = NewNode;
    }

    /*不可修改原链表的代码*/
    while (p1 != nullptr)
    {
        int min = p1->m_nValue;
        p1 = p1->m_pNext;
        //创建新节点
        NewNode = new ListNode();
        NewNode->m_nValue = min;
        NewNode->m_pNext = nullptr;

        //连接
        previously->m_pNext = NewNode;

        //更新链表尾指针
        previously = NewNode;
    }

    /*不可修改原链表的代码*/
    while (p2 != nullptr)
    {
        int min = p2->m_nValue;
        p2 = p2->m_pNext;
        //创建新节点
        NewNode = new ListNode();
        NewNode->m_nValue = min;
        NewNode->m_pNext = nullptr;

        //连接
        previously->m_pNext = NewNode;

        //更新链表尾指针
        previously = NewNode;
    }
    return head->m_pNext;
}


int main()
{
    /*测试用例1*/
    // 1 3 5 7       2 4 6 8
    ListNode* Node1 = CreateNode(1);
    ListNode* Node3 = CreateNode(3);
    ListNode* Node5 = CreateNode(5);
    ListNode* Node7 = CreateNode(7);

    ListNode* Node2 = CreateNode(2); 
    ListNode* Node4 = CreateNode(4); 
    ListNode* Node6 = CreateNode(6);
    ListNode* Node8 = CreateNode(8);

    connect(Node1, Node3);
    connect(Node3, Node5);
    connect(Node5, Node7);

    connect(Node2, Node4);
    connect(Node4, Node6);
    connect(Node6, Node8);

    cout << "链表1:";
    PrintList(Node1);
    cout << "链表2:";
    PrintList(Node2);

    ListNode* head = Merge(Node1, Node2);
    cout << "合并后的链表:";
    PrintList(head);
    cout << "--------------------------------------" << endl;

    /*测试用例2*/
    // 1 2 4      3 5 8  
    Node1 = CreateNode(1);
    Node2 = CreateNode(2);
    Node4 = CreateNode(4);

    Node3 = CreateNode(3);
    Node5 = CreateNode(5);
    Node8 = CreateNode(8);

    connect(Node1, Node2);
    connect(Node2, Node4);

    connect(Node3, Node5);
    connect(Node5, Node8);

    cout << "链表1:";
    PrintList(Node1);
    cout << "链表2:";
    PrintList(Node3);

    head = Merge(Node1, Node3);
    cout << "合并后的链表:";
    PrintList(head);


    return 0;
}

在这里插入图片描述

上述代码中发现创建新节点多次重复我们可以封装成一个函数

		int min = p2->m_nValue;
        p2 = p2->m_pNext;
        //创建新节点
        NewNode = new ListNode();
        NewNode->m_nValue = min;
        NewNode->m_pNext = nullptr;

        //连接
        previously->m_pNext = NewNode;

        //更新链表尾指针
        previously = NewNode;

优化后的不允许修改原链表代码
C++

#include <iostream>
using namespace std;

typedef int TElemType;//链表节点值的数据类型
struct ListNode
{
	TElemType m_nValue;
	ListNode* m_pNext;
};

ListNode* CreateNode(TElemType val)//创建节点
{
	ListNode* Node = new ListNode();
	Node->m_nValue = val;
	Node->m_pNext = nullptr;
	return Node;
}

void connect(ListNode* L1, ListNode* L2)//链接
{
	L1->m_pNext = L2;
}

void PrintList(ListNode* head)//输出
{
	while (head != nullptr)
	{
		cout << head->m_nValue;
		if (head->m_pNext != nullptr)
		{
			cout << "->";
		}
		else
		{
			cout << endl;
		}
		head = head->m_pNext;
	}
}

//创建并连接新节点
//此处传参必须要二级指针,因为要修改指针
void connNewNode(ListNode** previously, ListNode** p)
{
	//创建新节点
	ListNode* NewNode = new ListNode();
	NewNode->m_nValue = (*p)->m_nValue;
	NewNode->m_pNext = nullptr;

	//连接
	(*previously)->m_pNext = NewNode;

	//更新新链表尾指针
	*previously = NewNode;

	//更新原链表指针
	*p = (*p)->m_pNext;
}

ListNode* Merge(ListNode* pHead1, ListNode* pHead2)//合并俩链表
{
	if (nullptr == pHead1)
	{
		return pHead2;
	}

	if (nullptr == pHead2)
	{
		return pHead1;
	}

	ListNode* p1 = pHead1;
	ListNode* p2 = pHead2;

	ListNode* head = new ListNode();//新链表的表头指针
	head->m_nValue = 0;
	head->m_pNext = nullptr;
	ListNode* previously = head;//表尾指针


	while (p1 != nullptr && p2 != nullptr)
	{
		if (p1->m_nValue < p2->m_nValue)
		{
			connNewNode(&previously, &p1);
		}
		else
		{
			connNewNode(&previously, &p2);
		}
	}

	/*不可修改原链表的代码*/
	while (p1 != nullptr)
	{
		connNewNode(&previously, &p1);
	}

	/*不可修改原链表的代码*/
	while (p2 != nullptr)
	{
		connNewNode(&previously, &p2);
	}
	return head->m_pNext;
}


int main()
{
	/*测试用例1*/
	// 1 3 5 7       2 4 6 8
	ListNode* Node1 = CreateNode(1);
	ListNode* Node3 = CreateNode(3);
	ListNode* Node5 = CreateNode(5);
	ListNode* Node7 = CreateNode(7);

	ListNode* Node2 = CreateNode(2);
	ListNode* Node4 = CreateNode(4);
	ListNode* Node6 = CreateNode(6);
	ListNode* Node8 = CreateNode(8);

	connect(Node1, Node3);
	connect(Node3, Node5);
	connect(Node5, Node7);

	connect(Node2, Node4);
	connect(Node4, Node6);
	connect(Node6, Node8);

	cout << "链表1:";
	PrintList(Node1);
	cout << "链表2:";
	PrintList(Node2);

	ListNode* head = Merge(Node1, Node2);
	cout << "合并后的链表:";
	PrintList(head);
	cout << "--------------------------------------" << endl;

	/*测试用例2*/
	// 1 2 4      3 5 8  
	Node1 = CreateNode(1);
	Node2 = CreateNode(2);
	Node4 = CreateNode(4);

	Node3 = CreateNode(3);
	Node5 = CreateNode(5);
	Node8 = CreateNode(8);

	connect(Node1, Node2);
	connect(Node2, Node4);

	connect(Node3, Node5);
	connect(Node5, Node8);

	cout << "链表1:";
	PrintList(Node1);
	cout << "链表2:";
	PrintList(Node3);

	head = Merge(Node1, Node3);
	cout << "合并后的链表:";
	PrintList(head);

	return 0;
}

如果我们可以修改原链表,代码将更加简单。由于只修改了Merge函数,所以展示部分代码
C++

ListNode* Merge(ListNode* pHead1, ListNode* pHead2)//合并俩链表
{
	if (nullptr == pHead1)
	{
		return pHead2;
	}

	if (nullptr == pHead2)
	{
		return pHead1;
	}

	ListNode* p1 = pHead1;
	ListNode* p2 = pHead2;

	ListNode* head = new ListNode();//新链表的表头指针
	head->m_nValue = 0;
	head->m_pNext = nullptr;
	ListNode* previously = head;//表尾指针


	while (p1 != nullptr && p2 != nullptr)
	{
		if (p1->m_nValue < p2->m_nValue)
		{
			connNewNode(&previously, &p1);
		}
		else
		{
			connNewNode(&previously, &p2);
		}
	}

	/*可修改原链表的代码*/
	if (p1 != nullptr)
	{
		previously->m_pNext = p1;
	}

	/*可修改原链表的代码*/
	if (p2 != nullptr)
	{
		previously->m_pNext = p2;
	}

	return head->m_pNext;
}

在这里插入图片描述

本章完!

以上是关于剑指Offer - 面试题25:合并俩个排序的链表的主要内容,如果未能解决你的问题,请参考以下文章

剑指OFFER----面试题25. 合并两个排序的链表

剑指offer 面试25题

剑指Offer对答如流系列 - 合并两个排序的链表

[剑指offer]面试题17:合并两个排序的链表

剑指offer:面试题17合并两个排序的链表

剑指offer面试题17:合并两个排序的链表