LeeCode题目:单链表的排序

本文深入探讨了归并排序和单链表排序这两种算法。归并排序利用分治策略,通过递归将序列拆分成两部分再合并,时间复杂度为O(nlogn)。单链表排序则结合快慢指针找中点和递归,同样达到O(nlogn)的时间效率。代码实现分别展示了如何在数组和链表场景下应用这两种排序方法。


归并排序

归并排序是运用分治法解决问题的典型范例,基本思想是基于合并操作,即合并两个有序的序列是容易的,合并操作是可以在*O(m+n)*时间内完成。

归并排序的过程如下

  • 首先进行划分,将待排序列划分为大小相等(或大致相等)的两个子序列;
  • 当子序列的规模大于1时,递归排序子序列,直到子序列规模为1;
  • 将两个有序的子序列合并为一个有序序列。
  • 如下图所示,归并排序导致了一系列递归的调用,而这一系列的调用过程可以由一个二叉树表示,每个结点上端白色部分表示递归调用的输入,而下端为递归调用的输出,边上序号表示各个递归调用发生的次序。

归并排序

代码实现:

import java.util.Arrays;
public class MergeSort {
	//合并两个有序区间[p,q][q+1,r]
	public static void merge(int[] array, int p, int q, int r) {
		//定义一个存放合并后的数组
		int[] temp = new int[r-p+1];
		//设置两个区间的指针
		int s = p;
		int t = q+1;
		int k =0;
		while(s<=q && t<=r) {
			if(array[s]<array[t]) {
				temp[k++] = array[s++];
			} else {
				temp[k++] = array[t++];
			}
		}
		while(s<=q) temp[k++] = array[s++];
		while(t<=r) temp[k++] = array[t++];
		for(int i=0;i<temp.length;i++) {
			array[p+i] = temp[i];
		}
		System.out.println(Arrays.toString(temp));
	}
	//对[low,high]区间内排序
	public static void mergeSort(int[] array, int low, int high) {
		if(low<high) {
			mergeSort(array,low,(low+high)/2);
			mergeSort(array,(low+high)/2+1,high);
			merge(array,low,(low+high)/2,high);
		}
	}
	public static void main(String[] args) {
		int[] array = {4,8,9,5,2,1,4,6};
		mergeSort(array,0,array.length-1);
	}
}

归并排序为了将子序列合并,需要使用额外的存储空间,空间复杂度为O(n),时间复杂度为O(nlogn)

单链表排序

题目描述:给定一个结点数为n的无序单链表头结点head,对其按升序排列,要求时间复杂度为O(nlogn)
示例
输入:head = [4,2,1,3]
输出:[1,2,3,4]

利用归并排序,思路如下:

  • 首先找到当前链表中点(利用快慢双指针法,慢指针指向中点),并从中点将链表断开,得到两个链表;
  • 当两个链表的结点数大于1时,递归排序,直到链表节点数为1;
  • 最后进行两个 链表的合并。

代码实现:

import java.util.*;
/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */
public class Solution {
  
    public ListNode sortInList (ListNode head) {
        // write code here
        if(head == null || head.next == null){
            return head;
        }
        //使用快慢指针寻找链表的中点
        ListNode slow = head,fast = head.next;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode tmp = slow.next;
        slow.next = null;
        ListNode left = sortInList(head);
        ListNode right = sortInList(tmp);
        ListNode h = new ListNode(0);
        ListNode res = h;
        //合并两个链表
        while(left != null && right != null){
            if(left.val < right.val){
                h.next = left;
                left = left.next;
            } else{
                h.next = right;
                right = right.next;
            }
            h = h.next;
        }
        h.next = left != null?left:right;
        return res.next;
    }
}
 

参考

数据结构与算法(JAVA语言版—周鹏)
如有侵权,请联系

### LeetCode 拓扑排序经典题目解析 #### 1. **LeetCode 207. 课程表** 此题的核心在于判断给定的课程安排是否存在一种有效的拓扑排序[^1]。如果能够找到这样的一种排列,则表示不存在环路,课程可以顺利完成;反之则不可行。具体实现过程中需要用到邻接矩阵或邻接链表来构建图结构,并通过Kahn算法(基于BFS)或者深度优先搜索(DFS)的方法来进行检测是否有环的存在。 ```cpp // Kahn's Algorithm Example Code Snippet for Problem 207 vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) { vector<vector<int>> adj(numCourses); vector<int> indegree(numCourses, 0); for(auto &p : prerequisites){ adj[p[1]].push_back(p[0]); ++indegree[p[0]]; } queue<int> q; for(int i=0;i<numCourses;++i){ if(indegree[i]==0)q.push(i); } vector<int> res; while(!q.empty()){ int cur=q.front();q.pop(); res.push_back(cur); for(auto next:adj[cur]){ --indegree[next]; if(indegree[next]==0)q.push(next); } } return res.size()==numCourses?res:vector<int>(); } ``` --- #### 2. **LeetCode 210. 课程表 II** 这是对前一问题的进一步深化,不仅要求判定是否存在有效路径,还需要返回具体的选课顺序[^2]。同样可以通过拓扑排序的思想解决这个问题,区别只在于最后的结果呈现形式有所改变——当且仅当所有节点都被遍历过后才认为找到了合适的解决方案。 --- #### 3. **LeetCode 269. 外星语言词典** 虽然表面上看这道题似乎与字符串处理更加相关,但实际上它的本质依然是一个典型的拓扑排序应用场景之一[^3]。通过对单词之间的字母比较建立起字符间的相对大小关系网之后,就可以运用同样的技巧去还原整个外星文字母表应有的正确次序了。 --- #### 4. **其他涉及拓扑排序的相关练习建议** 除了以上列举出来的几个较为经典的案例之外,还有不少其他的习题也值得大家尝试练习以便更好地理解和掌握这一知识点的实际运用能力。比如像[ArcWing]848那样的基础入门级训练项目就非常适合初学者用来熟悉基本概念和技术要点[^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值