有序链表和数组的合并

一:链表的合并

 思路分析1(双指针解法)

1:定义两个指针遍历l1,l2分别指向第一条链表的头结点list1和第二条链表的头结点list2;

2:定义一个空节点preNode,用于连接两条有序链表,定义一个结果指针指向空节点

3.1:当两条链表的头结点都为空是直接返回null即可

3.2:当两条链表都不为空时,循环比较节点值的大小,将值较小的节点挂载到结果指针之后,并将结果指针后移

3.3:当退出循环时,两条链表有一条为空时,将不为空的链表的所有节点挂载到结果指针之后即可

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        //定义一个头结点的前驱节点
        ListNode preNode=new ListNode(-1);
        //定义一个指针指向头结点的前驱结点,用于遍历新生成的链表
        ListNode resNode = preNode;
       // 定义一个指针l1指向头结点list1
        ListNode l1=list1;
        // 定义一个指针l2指向头结点list2
        ListNode l2=list2;
        //如果第一个条链表的头结点为空,那么直接返回第二条链表的头结点即可
        if(list1==null){
            return list2;
        }
        ////如果第二个条链表的头结点为空,那么直接返回第一条链表的头结点即可
        if(list2==null){
            return list1;
        }
        //l1 , l2有一个为空(即有一条链表遍历完毕之后)就退出循环
        while(l1!=null && l2!=null){
            //当第一条链表的头结点小于第二条链表的头结点时
            if(l1.val<l2.val){
                //将l1挂载到头结点的前驱结点之后
                resNode.next=l1;
                //指向第一条链表的指针后移
                l1=l1.next;
            }
            else {
                //否则l2挂载到前驱节点之后
                resNode.next=l2;
                //指针后移
                l2=l2.next;
            }
             //每循环一次,就将将指向前驱节点的指针后移
            resNode=resNode.next;
        }
            //当退出循环时,即两条链表至少有一条为空
            //如果第一条链表遍历完毕,为空时
             if(l1==null){
            //将第二条链表后的所有节点挂载到结果指针上
            resNode.next=l2;
            }
             //如果第二条链表遍历完毕,为空时
             if(l2==null){
            //将第一条链表后的所有节点挂载到结果指针上
            resNode.next=l1;
            }
        return preNode.next;
    }
}

思路分析2(递归解法)

1:先进行头结点是否为空的判断

2:比较两条链表头结点值的大小,如果list1<list2,将list2指向list1,并递归的开始比较list2与第一条链表去除掉list1之后的所有节点

3:如果list2<list1,将list1指向list2,并递归的开始比较list1与第二条链表去除掉list2之后的所有节点

​
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
     if(list1==null){
         return list2;
     }
     if(list2==null){
         return list1;
     }
     if(list1.val>=list2.val){
         list2.next=mergeTwoLists(list1,list2.next);
         return list2;
     }
     else{
         list1.next=mergeTwoLists(list1.next,list2);
         return list1;
     }
    }
}

​

二:数组的合并

 

思路分析:

1:需要建立一个新数组,大小为m+n;并定义nums1数组的下标变量nums1Index,nums2数组的下标变量nums2Index,新数组的下标变量index

2.1:当nums1数组中的m个元素全部取完时,将nums2数组中的所有元素全部添加到新数组中

2.2:当nums2数组中的n个元素全部取完时,将nums1数组中的所有元素全部添加到新数组中

2.3:如果nums1数组中第nums1Index个元素小于nums2数组中第nums2Index个元素时,将较小的值添加的新数组中,nums1Index后移

2.4:如果nums2数组中第nums2Index个元素小于nums1数组中第nums1Index个元素时,将较小的值添加的新数组中,nums2Index后移

注意:一定要先判断nums1数组和nums2数组是否全部取完,再比较两个数组内元素大小。

如果先判断两个数组内元素大小的话,如nums1 = [1,2,3,0,0,0],就会把3后面的0,0,0输入到新数组中,判断不了nums2数组中剩下的元素

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int k=m+n;
        //定义一个新数组,用于存储排序后的数组元素,数组大小为m+n
        int[] newArr=new int[k];
          int nums1Index=0;
            int nums2Index=0;
        //index指向新数组的第一个元素,下标为0
        for(int index=0;index<k;index++){
             //当nums1数组中所有元素取完时,将nums2数组中的所有元素添加的新数组中
             if(nums1Index >= m){
                newArr[index]=nums2[nums2Index++];
            }
            //当nums2数组中所有元素取完时,将nums1数组中的所有元素添加的新数组中
            else if(nums2Index >= n){
                newArr[index]=nums1[nums1Index++];
            }
            //当nums1数组元素小于nums2数组元素时
            else if(nums1[nums1Index]<nums2[nums2Index]){
                //把较小值添加到新数组中去
                newArr[index]=nums1[nums1Index];
                //并将指针后移
                nums1Index++;
            }
            // 当nums2数组元素小于nums1数组元素时
            else {
                newArr[index]=nums2[nums2Index];
                nums2Index++;
            }
        }
        //题目中要求存储到nums1数组中
        //遍历新数组中的所有元素,取出并存放到nums1中
        for(int i=0;i<k;i++){
            nums1[i]=newArr[i];
        }
    }
}

 

<think>嗯,用户想学习如何合并两个有序链表或有序数组的算法。我需要先回顾一下之前的相关回答引用内容。用户之前的问题是关于如何在数组中查找特定元素,现在转向合并操作,可能是在学习算法的基础知识,或者准备面试题。 首先,我需要确定用户的需求。他们可能希望了解合并有序数据结构的常见方法,比如迭代法、递归法,以及针对数组的不同处理方式。根据提供的引用,特别是引用3、4、5,有关于链表数组合并的不同方法,比如暴力解法、双指针、递归等。 用户提到的合并两个有序链表有序数组,可能需要分步骤讲解。对于链表,通常有迭代递归两种方法,而数组可能需要双指针从前往后或者从后往前的方法,特别是处理原地合并的情况,比如LeetCode 88题。 需要检查是否有用户可能存在的误区。例如,合并数组时,如果从前向后合并可能会覆盖元素,所以从后向前更高效。而链表的递归方法虽然简洁,但可能有栈溢出的风险,这时候迭代更安全。用户提供的引用5中提到了数组合并使用数组,但实际可能需要优化到O(1)空间,特别是当nums1有足够空间时。 还要注意代码示例的正确性,比如链表的指针操作,数组的索引处理。用户提供的引用中有Java、C++、Python的示例,可能需要统一用用户熟悉的语言,但用户之前的问题中有CPython的例子,所以可能需要多语言支持,但用户现在可能更关注算法本身。 需要结构清晰,分链表数组两部分,每种数据结构下再分方法,比如直接合并、递归等。同时,比较不同方法的优缺点,时间空间复杂度,适用场景。例如,引用4提到递归在链表较长时可能不安全,迭代更可靠。 可能还需要提到相关题目,如合并K个链表合并区间,但用户当前问题只涉及两个,所以作为扩展即可。最后,生成相关问题帮助用户巩固知识,比如时间复杂度的比较,优化方法,处理重复元素等。 总结下来,回答需要包括: 1. 合并有序链表的迭代递归方法,代码示例,复杂度分析。 2. 合并有序数组的双指针方法(从后向前),处理原地合并,代码示例。 3. 方法比较适用场景。 4. 相关问题,如优化、复杂度、扩展问题等。</think>### 合并两个有序链表数组的算法详解 #### 一、合并两个有序链表 有序链表合并链表操作中的经典问题,核心思路是通过指针逐步比较节点值,构建新链表。以下是两种主流实现方法: --- ##### 1. 迭代法(双指针) 通过两个指针遍历链表,选择较小值的节点插入结果链表,直至完成合并。 **代码实现(Python)**: ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def mergeTwoLists(l1: ListNode, l2: ListNode) -> ListNode: dummy = ListNode() # 哨兵节点简化操作 curr = dummy while l1 and l2: if l1.val < l2.val: curr.next = l1 l1 = l1.next else: curr.next = l2 l2 = l2.next curr = curr.next curr.next = l1 if l1 else l2 # 链接剩余节点 return dummy.next ``` **时间复杂度**:$O(n+m)$,空间复杂度:$O(1)$(仅修改指针)[^4]。 --- ##### 2. 递归法 递归法通过比较当前节点值,选择较小节点作为当前节点,并递归合并剩余部分。 **代码实现(Java)**: ```java public ListNode mergeTwoLists(ListNode list1, ListNode list2) { if (list1 == null) return list2; if (list2 == null) return list1; if (list1.val < list2.val) { list1.next = mergeTwoLists(list1.next, list2); return list1; } else { list2.next = mergeTwoLists(list1, list2.next); return list2; } } ``` **时间复杂度**:$O(n+m)$,空间复杂度:$O(n+m)$(递归栈)[^4]。 --- #### 二、合并两个有序数组 数组合并的核心是双指针技巧,通常需从后向前填充以避免覆盖原数据。 ##### 双指针法(从后向前) **适用场景**:数组1有足够空间容纳合并后的所有元素(如LeetCode 88题)。 **代码实现(Python)**: ```python def merge(nums1, m, nums2, n): p1, p2, p = m-1, n-1, m+n-1 # 指针从后向前 while p1 >= 0 and p2 >= 0: if nums1[p1] > nums2[p2]: nums1[p] = nums1[p1] p1 -= 1 else: nums1[p] = nums2[p2] p2 -= 1 p -= 1 nums1[:p2+1] = nums2[:p2+1] # 处理剩余元素 ``` **时间复杂度**:$O(n+m)$,空间复杂度:$O(1)$[^5]。 --- #### 三、方法对比与选择 | 数据结构 | 方法 | 优点 | 缺点 | |----------|------------|-----------------------|-----------------------| | 链表 | 迭代法 | 空间效率高,栈安全 | 代码稍长 | | 链表 | 递归法 | 代码简洁 | 栈溢出风险(长链表) | | 数组 | 双指针法 | 原地操作,无需额外空间| 需数组预留足够空间 | --- #### 四、扩展应用 1. **合并K个有序链表**:使用优先队列(最小堆)优化时间复杂度至$O(N \log K)$[^2]。 2. **合并有序区间**:需先排序后合并重叠区间(如LeetCode 56题)。 --- ### 相关问题 1. **迭代法递归法在合并链表时的时间复杂度是否相同?为什么递归法的空间复杂度更高?** 2. **如果合并两个数组时不允许使用额外空间,如何优化算法?** 3. **合并K个有序链表时,如何利用分治思想降低时间复杂度?** 4. **在处理链表的递归合并时,如何避免栈溢出问题?** 5. **为什么合并数组时从后向前遍历比从前向后更高效?**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值