【数据结构】--oj_合并两个有序链表(详解)

目录

方法一:无头结点的方法 

方法二:有头结点的方法


题目链接:合并两个有序链表

方法一:无头结点的方法 

思路:依次比较链表中的节点,每次取小的节点,尾插到新的链表后面。

问题一:找到较小的节点后是不是就要找到尾节点进行尾插,难道每次尾插完都找尾节点吗?效率肯定很低,所以可以用一个指针tail来存尾节点。再创建一个指针head用来表示另一个没有头结点的新链表。 后续的尾插就可以尾插到head链表中

问题二:单链表是否有头结点,有头结点是一种情况,没有头结点就是另一种情况,相比而言有头节点的方式更简单,oj题一般都是默认链表无头结点的,但是我们可以创建头结点,所以就有两种方法

 代码如下:

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
	//如果其中一个为空,就返回另一个
	if (l1 == NULL)
	{
	//如果刚开始就有一个为空
	//那就直接返回另一个
	//如果两个都是NULL,那正好返回NULL就好了
		return l2;
	}
	if (l2 == NULL)
	{
		return l1;
	}
	struct ListNode* head = NULL, * tail = NULL;
	while (l1 && l2)
	{//循环走下去的条件就是l1和l2均不为NULL
	//如果其中一个为空了,就说明走完了
		if (l1 ->val < l2->val)
		{
			if (head == NULL)
			{
			//第一次尾插的话,我们是在新链表无头结点的情况下
			// 后续的插入才会在有节点的情况,
			// 而这两种情况代码不同,所以要分类讨论
			//所以直接使头和尾被赋值为l1
			//tail主要是用来找尾节点的
				head = tail = l1;
			}
			else
			{
			//进入else,说明新创的链表中已经有节点,所以
			//直接插入在后面的节点
				tail->next = l1;//尾插节点
				tail = l1;//tail用来保存尾节点
			}
			l1 = l1->next;//每次尾插完一个数据,l1会往后走
		}
		else
		{
			if (head == NULL)
			{
				//第一次尾插
				head = tail = l2;
			}
			else
			{
				tail->next = l2;
				tail = l2;
			}
			l2 = l2->next;
		}
	}
	if (l1)
	{//如果l1不为空,那么就是l2走完了
	//此时直接把l1剩余部分尾插到新链表中即可
		tail->next = l1;
	}
	if (12)
	{
		tail->next = l2;
	}
	return head;
}

 上段代码的缺点就是while循环中对于l1和l2有几处重复部分,所以可以在上段代码的基础上再修改一下,思路一样,head原来为NULL,那我就先找一个最小的让head指向这个节点就行,就不用在while循环中l1和l2先判断if才行,下面是更简洁的代码:

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
	//如果其中一个为空,就返回另一个
	if (l1 == NULL)
	{
		//如果刚开始就有一个为空
		//那就直接返回另一个
		//如果两个都是NULL,那正好返回NULL就好了
		return l2;
	}
	if (l2 == NULL)
	{
		return l1;
	}
	struct ListNode* head = NULL, * tail = NULL;
	//先取两个链表中一个小的去做第一个
	//能做第一个,因为两个链表都是升序的
	if (l1->val < l2->val)
	{
		head = tail = l1;
		l1 = l1->next;
	}
	if (l1->val > l2->val)
	{
		head = tail = l2;
		l2 = l2->next;
	}
	while (l1 && l2)
	{//循环走下去的条件就是l1和l2均不为NULL
	//如果其中一个为空了,就说明走完了
		if (l1->val < l2->val)
		{
			tail->next = l1;//尾插节点
			tail = l1;//tail用来保存尾节点
			l1 = l1->next;//每次尾插完一个数据,l1会往后走
		}
		else
		{
			tail->next = l2;
			tail = l2;
			l2 = l2->next;
		}
	}
	if (l1)
	{//如果l1不为空,那么就是l2走完了
	//此时直接把l1剩余部分尾插到新链表中即可
		tail->next = l1;
	}
	if (l2)
	{
		tail->next = l2;
	}
	return head;
}

方法二:有头结点的方法

原来我们题目给的是没有头结点的,但是我们可以自己创建一个头结点,他不存储数据,他是为了使后面找元素方便,值得注意的是这个head和tail都要动态内存开辟一个,最后在用完之后还需要释放,并且最后不能直接return head,要return head->next,因为题目本来就没给你头结点,你就不能返回你自己创建的头,本质上应该从head->next开始

#include<stdio.h>
#include<stdlib.h>
struct ListNode
{
	struct ListNode* next;
	int val;
};
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
	//如果其中一个为空,就返回另一个
	if (l1 == NULL)
	{
		//如果刚开始就有一个为空
		//那就直接返回另一个
		//如果两个都是NULL,那正好返回NULL就好了
		return l2;
	}
	if (l2 == NULL)
	{
		return l1;
	}
	struct ListNode* head = NULL, * tail = NULL;
	//哨兵位的头结点,head和tail刚开始均指向头结点
	head = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
	while (l1 && l2)
	{//循环走下去的条件就是l1和l2均不为NULL
	//如果其中一个为空了,就说明走完了
		if (l1->val < l2->val)
		{
			tail->next = l1;//尾插节点
			tail = l1;//tail用来保存尾节点
			l1 = l1->next;//每次尾插完一个数据,l1会往后走
		}
		else
		{
			tail->next = l2;
			tail = l2;
			l2 = l2->next;
		}
	}
	if (l1)
	{//如果l1不为空,那么就是l2走完了
	//此时直接把l1剩余部分尾插到新链表中即可
		tail->next = l1;
	}
	if (l2)
	{
		tail->next = l2;
	}
	struct ListNode* list = head->next;
	free(head);
	return list;
}

最后更新下有头结点的C++和Java代码

C++代码:

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if (list1 == nullptr) return list2;
        if (list2 == nullptr) return list1;
        ListNode* head = new ListNode();
        ListNode* tail = head;
        
        while(list1 && list2){
            if (list1->val < list2->val){
                tail->next = list1;
                tail = tail->next;
                list1 = list1->next;
            } else{
                tail->next = list2;
                tail = tail->next;
                list2 = list2->next;
            }
        }
        if (list1) tail->next = list1;
        if (list2) tail->next = list2;
        ListNode* newHead = head->next;
        return newHead;
    }
};

Java代码:

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if (list1 == null) return list2;
        if (list2 == null) return list1;
        ListNode head = new ListNode();
        ListNode tail = head;
        
        while(list1 != null && list2 != null){
            if (list1.val < list2.val){
                tail.next = list1;
                tail = tail.next;
                list1 = list1.next;
            } else{
                tail.next = list2;
                tail = tail.next;
                list2 = list2.next;
            }
        }
        if (list1 != null) tail.next = list1;
        if (list2 != null) tail.next = list2;
        ListNode newHead = head.next;
        return newHead;
    }
}

  • 15
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值