合并两个有序链表——递归实现和非递归实现

本文介绍了一种合并两个已排序链表的算法,并提供了C和Java两种语言的实现方式。通过递归和非递归方法,将两个递增有序链表合并成一个有序链表。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      先看代码的实现:

#include<stdio.h>
#include<stdlib.h>
//链表存储结构
typedef struct Node
{
	int number;
	struct Node * next;
}Node;
typedef struct Node * LinkList;

void createListHead(LinkList * pHead, int option) //形参pHead为指向指针的指针 
{
	LinkList p, r;
	int i;
	*pHead = (LinkList)malloc(sizeof(Node));   //头结点,不存数据 
	r = *pHead;
	if (option == 1)  //生成奇数递增链表
	{
		for (i = 1; i < 11; i += 2)
	    {
			p = (LinkList)malloc(sizeof(Node));
			p->number = i;
			r->next = p;
			r = p;
	    }	
	} 
	else if (option == 2)   //生成偶数递增链表 
	{
		for (i = 2; i < 10; i += 2)
	    {
			p = (LinkList)malloc(sizeof(Node));
			p->number = i;
			r->next = p;
			r = p;
	    }		
	} 

	r->next = NULL;
}    

void displayElem(LinkList pHead)
{
	while (pHead)
	{
		printf("%d\t", pHead->number);
		pHead = pHead->next;	
	}
	printf("\n");
}

LinkList mergedList(LinkList pHead1, LinkList pHead2)
{
	LinkList pHead = NULL;
	if (pHead1 == NULL)
	{
		return pHead2;	
	}
	else if (pHead2 == NULL)
	{
		return pHead1;	
	}
	
	if (pHead1->number < pHead2->number)
	{
		pHead = pHead1;
		pHead->next = mergedList(pHead1->next, pHead2);	
	}
	else
	{
		pHead = pHead2;
		pHead->next = mergedList(pHead1, pHead2->next);	
	}
	return pHead;
	
}
int main()
{
	LinkList pHead1 = NULL;
	LinkList pHead2 = NULL;
	LinkList pHead = NULL;
	createListHead(&pHead1, 1);   /*传入指针本身的地址,这里读者得好好想想,为什么这样 
	                               *相当于是传入pHead1这个指针本身的地址,然后在
								   *createListHead函数中操纵这个指针pHead1,让它有指向
								   */ 
	createListHead(&pHead2, 2); 
	displayElem(pHead1->next);
	displayElem(pHead2->next);    //pHead1和pHead2就是两个排好序的链表 
	
	//下面要将这两个排好序的链表组合成一个有序的递增链表 
	pHead = mergedList(pHead1->next, pHead2->next);
	displayElem(pHead);
	return 0;
}
程序运行显示结果:


     两个有序递增的链表pHead1和pHead2,如果pHead1的第一个结点的值大于pHead2的第一个结点的值,就将pHead1的头结点赋值给pHead。然后进行下一次的比较,反之就是pHead2赋给pHead。如图:

    
   这就是一个递归的过程,当到链表的最后一个结点的位置后,递归结束,依次返回各个递归函数的结点,最后组合成一个链表——合并之后的有序链表。

    关于链表的知识,亲可以去查看《大话数据结构》哦!

后边在一个公司面试Java职位的时候,当时让我手写这个算法,用的是非递归的方式,看Java代码: 

public class MergeSortedLinkedList {
    public class Node {
        private int number;
        private Node next;
        Node(int number, Node next) {
            this.number = number;
            this.next = next;
        }
    }

    /**
     * 返回头节点
     * @param isOdd
     * @return
     */
    public Node createSortedLinkedList(boolean isOdd) {
        Node p, head;
        // 头结点
        p = head = new Node(0, null);
        if (isOdd) {
            // 创建奇数有序链表
            for (int i = 1; i < 20; i += 2) {
                Node node = new Node(i, null);
                p.next = node;
                p = node;
            }
        } else {
            // 创建偶数有序链表
            for (int i = 2; i < 20; i += 2) {
                Node node = new Node(i, null);
                p.next = node;
                p = node;
            }
        }
        return head;
    }

    public Node mergeTwoSortedLinkedList(Node head1, Node head2) {
        // 注意参数判断,这个面试官也很看重 
        if (null == head1) {
            return head2;
        }
        if (null == head2) {
            return head1;
        }

        // 给新merge的头结点设值
        Node p, head;
        if (head1.number < head2.number) {
            head = head1;
            head1 = head1.next;
        } else {
            head = head2;
            head2 = head2.next;
        }
        p = head;

        while (null != head1 && null != head2) {
            if (head1.number < head2.number) {
                p.next = head1;
                // p指针向下移动到新结点
                p = head1;
                head1 = head1.next;
            } else {
                p.next = head2;
                p = head2;
                head2 = head2.next;
            }
        }
        // 对剩余结点的处理 
        if (null != head1) {
            p.next = head1;
        }
        if (null != head2) {
            p.next = head2;
        }
        return head;
    }

    public void display(Node node) {
        while (null != node) {
            System.out.print(node.number + " ");
            node = node.next;
        }
        System.out.println();
    }

    public static void main(String[] args) {
        MergeSortedLinkedList sortedLinkedList = new MergeSortedLinkedList();
        Node head1 = sortedLinkedList.createSortedLinkedList(true);
        Node head2 = sortedLinkedList.createSortedLinkedList(false);
        System.out.print("Linked list one: ");
        sortedLinkedList.display(head1.next);
        System.out.print("Linked list two: ");
        sortedLinkedList.display(head2.next);
        System.out.print("Merged linked List: ");
        sortedLinkedList.display(sortedLinkedList.mergeTwoSortedLinkedList(head1.next, head2.next));
    }
}


### 合并两个有序数组或链表的算法实现 #### 数组合并 对于两个有序数组 `nums1` `nums2`,可以采用从后向前的双指针方法来完成合并操作。这种方法充分利用了数组已经排序的特点以及目标数组的空间特性。 以下是具体实现: ```python def merge(nums1, m, nums2, n): last = m + n - 1 # 合并后的最后一个位置索引 i, j = m - 1, n - 1 # 初始化两个数组的有效部分的最后一位索引 while i >= 0 and j >= 0: # 当两个数组都有剩余元素时 if nums1[i] > nums2[j]: nums1[last] = nums1[i] i -= 1 else: nums1[last] = nums2[j] j -= 1 last -= 1 # 如果 nums2 中还有剩余元素,则将其复制到 nums1 剩余的位置上 while j >= 0: nums1[last] = nums2[j] last -= 1 j -= 1 ``` 上述代码通过逆向遍历的方式避免了覆盖原数据的风险,并且时间复杂度为 \(O(m+n)\),其中 \(m\) 是第一个数组有效长度,\(n\) 是第二个数组的长度[^1]^。 --- #### 链表合并 针对两个排序的单链表,可以通过递归或者迭代的方法构建一个新的非递减链表。这里提供一种基于迭代的解决方案: ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def mergeTwoLists(l1, l2): dummy = ListNode() # 创建虚拟头节点简化边界情况处理 current = dummy # 定义当前工作节点指向dummy while l1 and l2: # 只要l1l2都不为空就继续循环 if l1.val <= l2.val: current.next = l1 l1 = l1.next else: current.next = l2 l2 = l2.next current = current.next # 更新current至下一个节点 # 将可能存在的剩余部分链接上去 if l1 is not None: current.next = l1 elif l2 is not None: current.next = l2 return dummy.next # 返回真正的head node (跳过dummy) ``` 此段程序同样具有线性的运行效率 \(O(n+m)\)[^2]^ ,其中 \(n,m\) 分别代表两输入列表长度。 --- #### 总结对比 无论是数组还是链表,在它们均为升序排列的前提下都可以借助双指针技术高效达成目的;不过由于存储介质差异——连续内存块 vs 节点连接关系——使得各自适用的具体策略有所区别。前者倾向于直接修改现有容器内的数值达到最终状态而无需额外开辟空间;后者则需创建新实例或将旧有对象重新串联起来形成期望的结果集[^3]^。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值