首页 > 其他分享 >LeetCode —— 排序

LeetCode —— 排序

时间:2023-08-26 21:46:52浏览次数:44  
标签:head 排序 ListNode fast next slow null LeetCode

148. 排序链表

一般都用归并排序,因为是单向链表,其它排序算法根据下标找元素,向前遍历等都比较困难

主函数流程是:

  • 如果 head==null || head.next==null return head。因为 head.next == null 即只有一个元素时,不用再划分了,而且一个元素本身也是有序的,所以返回就返回这一个元素
  • 通过找链表中点算法找到 midNode
  • 左半数组是 head~midNode,右半数组是 midNode.next~结尾null 。因此令 rightListHead=midNode.next。此外为了使坐半链表有正确的边界,结尾指向null,把链表从中间分割开来,令 midNode.next = null
  • 左半递归 rightHead = sortList(head) 右半递归  rightHead = sortList(rightListHead) 
  • 最后合并左半和右半两个有序链表 return merge2OrderList(leftHead, rightHead)

过程中会用到链表的两个经典算法:

  • 合并两个有序链表:虚拟头节点,ListNode head = new ListNode(0); curPre = head 最后返回 head.next。while(p1!=null || p2!=null) if(p1==null)......
  • 找链表中点:快指针走两步,慢指针走一步,但又一点于以前不同: 我们希望奇数个如 1 2 3 时返回 2,偶数个如 1 2 3 4 时返回 2 。因为我们认为 mid.next 是右半的起点,并且在这时候会令 mid.next = null。这与之前的不同,之前偶数个如 1 2 3 4 时希望返回 3

之前的找链表中点:奇数个如 1 2 3 时返回 2,偶数个如 1 2 3 4 时返回 3 。

private static ListNode findMidNode(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null) {
            fast = fast.next;
            if (fast != null) {
                fast = fast.next;
                slow = slow.next;
            }
        }
        return slow;
    }
  • 奇数 1 2 3:初始 fast=1 slow=1 ;第一次循环:fast=2 fast=3 slow=2  ;第二次循环 fast=3 ;最后返回 slow=2
  • 偶数 1 2 3 3:初始 fast=1 slow=1 ;第一次循环:fast=2 fast=3 slow=2 ;第二次循环 fast=4 fast=null slow=3 ;最后返回 slow=3

现在的找链表中点:奇数个如 1 2 3 时返回 2,偶数个如 1 2 3 4 时返回 2 。

private static ListNode findMidNode(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null) {
            fast = fast.next;
            // 这里要上一个  && fast.next != null 的条件
            if (fast != null && fast.next != null) {
                fast = fast.next;
                slow = slow.next;
            }
        }
        return slow;
    }
  • 奇数 1 2 3:初始 fast=1 slow=1 ;第一次循环:fast=2 fast=3 slow=2 ;第二次循环 fast=3 ;最后返回 slow=2
  • 偶数 1 2 3 3:初始 fast=1 slow=1;第一次循环:fast=2 fast=3 slow=2 ;第二次循环 fast=4 因为fast.next==null退出了循环;最后返回 slow=2
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {

    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null) {
            // head.next == null 即只有一个元素时,不用再划分了,而且一个元素本身也是有序的,所以返回就返回这一个元素
            // 而且只有一个元素时,也没法找中点
            return head;
        }
        ListNode midNode = findMidNode(head);
        // 右半数组的起始是中间节点的下一个
        ListNode rightListHead = midNode.next;
       // 为了使链表有正常的边界,从中间节点断开
        midNode.next = null;
        ListNode leftHead = sortList(head);
        ListNode rightHead = sortList(rightListHead);
      // 合并两个有序链表
      ListNode list = merge2OrderList(leftHead, rightHead);
     return list;
    }

    /*
        经典链表算法:合并两个有序链表
     */
    private static ListNode merge2OrderList(ListNode head1, ListNode head2) {
        // 这个 head 是可以造的,只是为了后面可以不对头节点做特殊判断
        ListNode head = new ListNode(0);
        ListNode curPre = head;
        ListNode p1 = head1;
        ListNode p2 = head2;
        // 两个有一个没完,就继续循环,注意是 || 不是 &&
        while (p1 != null || p2 != null) {
            // list1完了。list2没完
            // 请注意:【刚开始写成了p1!=null】 应该是【p1==null】
            if (p1 == null) {
                curPre.next = p2;
                curPre = p2;
                p2 = p2.next;
            }
            // list2完了。list1没完
            else if (p2 == null) {
                curPre.next = p1;
                curPre = p1;
                p1 = p1.next;
            }
            else {
                if (p1.val <= p2.val) {
                    curPre.next = p1;
                    curPre = p1;
                    p1 = p1.next;
                }
                else {
                    curPre.next = p2;
                    curPre = p2;
                    p2 = p2.next;
                }
            }
        }
        // 最后返回 head.next,因为 head 就是个虚拟结点
        return head.next;
    }

    /*
        经典链表算法:合并两个有序链表
     */
    private static ListNode findMidNode(ListNode head) {
        // 我们希望奇数个如 1 2 3 时返回 2,偶数个如 1 2 3 4 时返回 2
        // 因为我们认为 mid.next 是右半的起点,并且在这时候会令 mid.next = null
        // 这与之前的不同,之前偶数个如 1 2 3 4 时希望返回 3
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null) {
            fast = fast.next;
            // 所以这里要上一个  && fast.next != null 的条件
            if (fast != null && fast.next != null) {
                fast = fast.next;
                slow = slow.next;
            }
        }
        return slow;
    }
}

 

215. 数组中的第K个最大元素

堆排序

class Solution {
    public int findKthLargest(int[] nums, int k) {
        int N = nums.length;
        // 注意这里是从 N/2-1 开始调,它的孩子已经可以包含堆的最后一个元素了
        // 注意 i>=0,因为堆顶可能也不满足堆定义
        for (int i=N/2-1;i>=0;i--) {
            heapfy(nums, N, i);
        }
        for (int i=N-1;i>=N-k;i--) {
            // 把末位元素提到顶部下标为0,堆顶最大放到末尾
            swap(nums, 0, i);
            // 现在这个新提上来的顶部下标为0的元素,不满足堆的定义,因此要调整0的位置。堆的边界是i,调整0。
            heapfy(nums, i, 0);
        }
        return nums[N-k];
    }

    private void heapfy(int[] nums, int N, int i) {
        // 先令最大的等于i
        int largest = i;
        // 左孩子
        int leftChilrd = 2*i+1;
        // 右孩子
        int rightChilrd = 2*i+2;
        // 左孩子比 largest 大
        if (leftChilrd < N && nums[leftChilrd] > nums[largest]) {
            largest = leftChilrd;
        }
        // 右孩子比 largest 大
        if (rightChilrd < N && nums[rightChilrd] > nums[largest]) {
            largest = rightChilrd;
        }
        // 说明左右孩子比 i 大,i 所处三角不满足堆的定义
        if (largest != i) {
            // 和更大的那个交换
            swap(nums, largest, i);
            // 继续调整 i 的位置(现在已经换到了largest),直到到一个满足堆定义的地方
            heapfy(nums, N, largest);
        }
    }

    private void swap(int[] nums, int a, int b) {
        int tmp = nums[a];
        nums[a] = nums[b];
        nums[b] = tmp;
    }
}

 

 

 

标签:head,排序,ListNode,fast,next,slow,null,LeetCode
From: https://www.cnblogs.com/suBlog/p/17659492.html

相关文章

  • 【LeetCode回溯算法#12】二叉树的直径,树形dp的前置内容(使用dfs)
    二叉树的直径给你一棵二叉树的根节点,返回该树的直径。二叉树的直径是指树中任意两个节点之间最长路径的长度。这条路径可能经过也可能不经过根节点root。两节点之间路径的长度由它们之间边数表示。示例1:输入:root=[1,2,3,4,5]输出:3解释:3,取路径[4,2,1,3]或......
  • 【LeetCode动态规划#17】知道秘密的人,维护多个dp数组
    知道秘密的人数在第1天,有一个人发现了一个秘密。给你一个整数delay,表示每个人会在发现秘密后的delay天之后,每天给一个新的人分享秘密。同时给你一个整数forget,表示每个人在发现秘密forget天之后会忘记这个秘密。一个人不能在忘记秘密那一天及之后的日子里分享......
  • P2824 排序(二分答案加线段树)
    传送门很巧妙的一个题直接排序肯定会\(T\)飞我们发现问题只有一个:第\(q\)个位置上的数字不难想到从这里入手,二分答案,考虑第\(q\)个位置上的数字是什么,不妨设他为\(x\)然后把大于等于\(x\)的数变成\(1\),小于\(x\)的数变为\(0\),把他转换为一个\(01\)排序问题:对于区间\([l,r]\)......
  • LeetCode 463.岛屿的周长
    1.题目:给定一个 rowxcol 的二维网格地图 grid ,其中:grid[i][j]=1 表示陆地, grid[i][j]=0 表示水域。网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。岛屿中没有“湖”(......
  • Leetcode 228. 汇总区间
    1.题目描述给定一个无重复元素的有序整数数组nums。返回恰好覆盖数组中所有数字的最小有序区间范围列表。也就是说,nums的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于nums的数字x。列表中的每个区间范围[a,b]应该按如下格式输出"a-......
  • LeetCode 131. 分割回文串
    题目链接:LeetCode131.分割回文串题意:给你一个字符串s,请你将s分割成一些子串,使每个子串都是回文串。返回s所有可能的分割方案。回文串是正着读和反着读都一样的字符串。解题思路:dfs算法的过程其实就是一棵递归树,所有的dfs算法的步骤大概有以下几步:找到中止条件:即......
  • LeetCode-26. 删除有序数组中的重复项(Java)
    这是我在51CTO博客开启的写作之路,第一次正式写博客记录我在LeetCode的刷题日,希望能帮助更多的小伙伴攻面自己心仪的公司offer。如下对于 LeetCode-26. 删除有序数组中的重复项,进行全面解析并小结解题思路,同学们请参考:1.题目描述给你一个 升序排列 的数组 nums ,请你 原地 删......
  • Leetcode 454. 四数相加 II(4sum ii)
    题目链接给你四个整数数组nums1、nums2、nums3和nums4,数组长度都是n,请你计算有多少个元组(i,j,k,l)能满足:0<=i,j,k,l<nnums1[i]+nums2[j]+nums3[k]+nums4[l]==0示例1:输入:nums1=[1,2],nums2=[-2,-1],nums3=[-1,2],nums4=[0,2]输出:2......
  • P4017 最大食物链计数 (DAG拓扑排序)
    空降锣鼓1题目分析首先,要知道这道题是Topo拓扑排序。不妨先从拓扑排序定义下手,分析题目的性质。经分析得:食物链中的生物——节点生物之间的关系——有向边为了方便描述,我们将不会捕食其他生物的生产者叫做最佳生产者不会被其他生物捕食的消费者叫做最佳消费......
  • P1113 杂务 (DAG拓扑排序--DP)
    这是一道拓扑排序的模板题0额.所需的前置知识:图论相关的基本概念建图,存图图的遍历非常入门的DP下面进入正文1引入拓扑排序是一类用于处理DAG(Directedacyclicgraph),即有向无环图上的问题。以这道题为例,我们分析拓扑排序的作用:显然地,本题中各项工作是有一定的依......