当前位置: 首页 > news >正文

【day11】技巧+链表

1、下一个排列 hot

题目:31. 下一个排列 - 力扣(LeetCode)

分析:就是找到下一个更大的数字。以 [1,3,5,4,2] 为例。

  • 第一步:从右向左,找到第一个数 x 小于其右邻数,作为较小数,示例是 3。为什么:因为我们想找一个较小数与其右边的较大数交换(让序列变大),那么 x 右边必须要有比它大的;交换后较大数的位置尽量靠右(让序列变大的幅度尽量小),那么就从右向左找第一个满足其右有较大数的较小数;因为是第一个其右有较大数的较小数,因此其右必是降序排列(其右的每个数的右边都不存在比每个数大的),那么我们只需要从右向左,用x的右邻数是否比x大,来判断 x 是否是第一个其右存在比它大的数的数(x 必是第一个破坏降序的数)。
  • 第二步:从右向左,找到第一个数 y 大于较小数 x,作为较大数,示例是 4。为什么:交换到前面的较大数应尽量小(让序列变大的幅度尽量小)。
  • 第三步:交换 x 与 y,最后翻转 y 后的子数组,示例结果 [1,4,2,3,5]。为什么:交换后,y 后的子数组必然保持降序(y 是最小的大于 x 的数,原位 y 其右的数必然比 x 小,原位 y 其左的数必然比 x 大),翻转后子数组为升序(让序列变大的幅度尽量小)。
  • 特殊情况考虑:降序数组是字典中最大的,只需要执行第三步翻转,就能回到最小的升序数组

        时间复杂度:找较小数x O(n),找较大数 y O(n),翻转 O(n),总体 O(n)。空间复杂度 O(1)。

代码

class Solution {public void nextPermutation(int[] nums) {// 1. 从右到左,找到第一个较小数 x:小于右邻数int i = nums.length-2;int x;while (i >= 0 && nums[i] >= nums[i+1]) i--; // 降序的情况,找不到 x,最后 i=-1if (i >= 0) x = nums[i]; // 降序没有 x i>=0// 2. 从右到左,找到第一个较大数 yint j = nums.length-1;int y;while(i >= 0 && j > i && nums[j] <= nums[i]) j--; // 降序没有 y i>=0if (i >= 0) {y = nums[j];swanp(nums, i, j); // 交换 x 和 y}// 3. 翻转 y 后的子数组 [i+1, n-1];若降序数组 i=-1,翻转全部 [0=i+1, n-1]reverse(nums, i+1, nums.length-1);}private void swanp(int[] nums, int i, int j) {int t = nums[i];nums[i] = nums[j];nums[j] = t;}private void reverse(int[] nums, int i, int j) {while (i < j) swanp(nums, i++, j--);}
}

2、多数元素 hot

题目:169. 多数元素 - 力扣(LeetCode)

分析

哈希表:哈希表统计每种数字的个数O(n),然后再遍历哈希表找到个数最大的元素(题目说必定存在多数元素)O(n)。时间复杂度 O(n),空间复杂度 额外哈希表 O(n)。

投票法:

  • 原则:多数元素(+1)的个数一定比其他元素(-1)总数多,那么最后计分必>0。
  • 每次假设剩下元素的首元素为多数元素,直到计数抵消为 0:

若假设正确,那么被消去的数中有一半是众数。

若假设错误,那么被消去的数中有 0 ~ 一半是多数元素(更多非众数元素被消掉,众数比例更多)。

这两种假设都不会打破原则。

  • 最终结果:剩余数组无法再抵消为0,计数大于0。若首元素不是众数,那么首元素还能被众数抵消为 0,所以最终剩余数组的首元素必然是众数。

        时间复杂度:O(n),空间复杂度 O(1)。

代码

哈希表:

class Solution {public int majorityElement(int[] nums) {// 统计每种元素的个数Map<Integer, Integer> counts = new HashMap<>();for (int num : nums) counts.put(num, counts.getOrDefault(num, 0)+1);// 遍历哈希表,找到计数中最大的数Map.Entry<Integer, Integer> max = null;for (Map.Entry<Integer, Integer> count : counts.entrySet()) {if (max == null || max.getValue() < count.getValue())max = count;}return max.getKey(); }
}

投票:

class Solution {public int majorityElement(int[] nums) {// 1. 假设首元素是众数,开始计数,众数计1,非众数计-1// 2. 抵消为 0 后执行 1.int cnt = 0, n = 0;for (int i = 0; i < nums.length; i++) {if (cnt == 0) n = nums[i];if (nums[i] == n) cnt++;else cnt--;}// 最终的首元素就是多数元素return n;}
}

3、合并两个有序链表 hot

题目:21. 合并两个有序链表 - 力扣(LeetCode)

分析:类似归并排序中,合并步骤。创建一个头指针,每次比较 i、j 节点哪个更小,小的尾插入链表。最后把剩下的一条全部入链表。

代码

/*** 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 mergeTwoLists(ListNode list1, ListNode list2) {ListNode head = new ListNode();ListNode k = head;while (list1 != null && list2 != null) {if (list1.val <= list2.val) {k.next = list1;list1 = list1.next;} else {k.next = list2;list2 = list2.next;}k = k.next;}if (list1 != null) k.next = list1;if (list2 != null) k.next = list2;return head.next;}
}

4、排序链表 hot

题目:148. 排序链表 - 力扣(LeetCode)

分析

法一,自顶向下归并排序(递归的方式):时间复杂度 O(nlogn),空间复杂度 递归深度 O(logn)。

法二,自底向上归并排序(迭代的方式):step 从 1 增加到 < arr.length,每次循环 step 翻倍。如 arr=[4, 2, 1, 3]。

① 第一次外循环,step=1。第一次内循环,分割出 4 和 2 排序 2,4;第二次内循环,分割出 1 和 3 排序 1,3。

② 第二次外循环,step=2*step=2。第一次内循环,分割出 2,4 和 1,3 排序 1,2,3,4。

③   step = 2*step = 4 >= arr.length,停止排序。

时间复杂度:计算长度O(n),每轮 step,划分数组 O(n),合并数组 O(n)。一共 O(logn) 个不同 step,综合O(n+2nlogn)=O(nlogn);空间复杂度:没有递归 O(1)。

代码

自顶向下归并排序:

/*** 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 {// 快慢指针,快指针走到了结尾时,慢指针指向终点private ListNode getMiddleNode(ListNode head) {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 pre = slow; // 中点slow = slow.next; // list2 链头pre.next = null; // 断开连接 return slow; // 返回list2链头}public ListNode sortList(ListNode head) {if (head == null || head.next == null) return head; // 不需要排序了// 划分,左右排序ListNode list2 = getMiddleNode(head);ListNode list1 =  sortList(head);list2 = sortList(list2);// 合并return merge(list1, list2);}private ListNode merge(ListNode list1, ListNode list2) {ListNode head = new ListNode();ListNode k = head;while (list1 != null && list2 != null) {if (list1.val <= list2.val) {k.next = list1;list1 = list1.next;} else {k.next = list2;list2 = list2.next;}k = k.next;}if (list1 != null) k.next = list1;if (list2 != null) k.next = list2;return head.next;}   
}

自底向上归并排序:

/*** 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 {// 2. 自底向上public ListNode sortList(ListNode head) {// 求链表长度int length = getLength(head);// 外循环,控制 stepListNode newHead = head; // 指向每一次新链表的链头for (int step = 1; step < length; step *= 2) {ListNode tmp = newHead; // 指向下一个划分的链头ListNode newTail = null; // 新链表链尾// 内循环,对于当前 step 划分,控制是否继续划分并排序(下一个划分头为 null 表示没有可排序的子数组)while (tmp != null) {// 划分出 list1 和 list2ListNode list1 = tmp;ListNode list2 = getStepList(list1, step);tmp = getStepList(list2, step);// 合并左右子数组,排序。返回新链表头、尾ListNode[] newHeadTail = merge(list1, list2);if (newTail == null) { // 新链表的第一个部分,更新头和尾newHead = newHeadTail[0];newTail = newHeadTail[1];}else { // 新链表的后续部分,头不需要更新,尾连接下一个合并后的头,然后更新尾newTail.next = newHeadTail[0];newTail = newHeadTail[1];}}}return newHead;}// 划分出一个子数组后,返回下一个划分头 private ListNode getStepList(ListNode head, int step) {// 如果是空数组,直接返回 nullif (head == null) return null;// 获取子数组最后一个节点;也可能没有下一个划分了,由第二个条件退出,返回 nullwhile (step != 1 && head.next != null) {head = head.next;step--;}// 当前划分尾指向 null,返回下一个划分头ListNode ret = head.next;head.next = null;return ret;}private ListNode[] merge(ListNode list1, ListNode list2) {ListNode newHead = new ListNode();ListNode newTail = newHead;while (list1 != null && list2 != null) {if (list1.val <= list2.val) {newTail.next = list1;list1 = list1.next;} else {newTail.next = list2;list2 = list2.next;}newTail = newTail.next;}if (list1 != null) newTail.next = list1;if (list2 != null) newTail.next = list2;// 让尾指针走到合并后的链表尾部while (newTail.next != null) newTail = newTail.next;return new ListNode[]{newHead.next, newTail};}private int getLength(ListNode head) {int count = 0;while (head != null) {count++;head = head.next;}return count;}
}

        其实实际的执行效率,时间上法一更短,空间上法二更少,这就取决于更需要时间还是空间了。

http://www.dtcms.com/a/533388.html

相关文章:

  • 临汾做网站长沙网站优化推广方案
  • 网站建设代理政策网站支持qq登录怎么做
  • 网站后缀ga蜂鸟影院高清免费观看
  • 童装网站建设目标分销系统合法吗
  • 如何解决pytorch下载缓慢问题
  • 广州小网站建设小网站广告投放
  • 网站制作哪个好一些电商营销策略方案
  • Ubuntu24.04
  • 广州市网站建设服务机构中山哪里有做网站
  • 网站模板 php沈阳网站建设q479185700棒
  • 学校培训网站开发做啊网站
  • CodeBuddy助力开发:从想法到落地的全流程体验
  • 1.4.1 大数据方法论与实践指南-元数据治理
  • 广东省省考备考(第一百二十六天10.17)——申论(第六节课)
  • 有个网站做字的图片qq登录wordpress
  • 005-Spring AI Alibaba Structured Output 功能完整案例
  • 私密性最好的浏览器营销网站优化推广
  • 电商网页精品欣赏网站企业管理培训课程机构
  • 中国住房建设网官方网站博客主题 wordpress
  • TVM | TupleNode / TupleGetItemNode
  • 什么做网站统计好杭州百度网站建设
  • 一流的网站建设与优化wordpress更改上传
  • now9999网站提示建设中网站制作怎么做下拉菜单
  • 深度学习周报(10.20~10.26)
  • 通用抓取算法AnyGrasp(Graspnet)——本地部署并测试自定义输入数据
  • 1.2.2 大数据方法论与实践指南-数据助力业务场景
  • php做的直播网站烟台网站制作这
  • 1.模拟算法
  • 昆明优化网站wordpress用户注册插件
  • 若依框架学习Day02:功能改造与问题攻坚实战