• 【刷题笔记】之牛客面试必刷TOP101(1)


    目录

    1. 反转链表(双链表头插法 / 栈)

    2.链表内指定区间反转

    3. 链表中的节点每k个一组翻转

    4. 合并两个排序的链表

    5. 合并k个已排序的链表 


    链接 :牛客面试必刷TOP101

    1. 反转链表(双链表头插法 / 栈)

    题目链接 反转链表_牛客题霸_牛客网 (nowcoder.com)

    题目要求

    题目分析(新建链表头插法

    1. public class Solution {
    2. public ListNode ReverseList(ListNode head) {
    3. ListNode newHead = null;
    4. while(head != null) {
    5. ListNode tmp = head.next;
    6. //使用头插法
    7. head.next = newHead;
    8. newHead = head;
    9. head = tmp;
    10. }
    11. return newHead;
    12. }
    13. }

    题目分析(栈)

    1. import java.util.Stack;
    2. public class Solution {
    3. public ListNode ReverseList(ListNode head) {
    4. Stack stack = new Stack<>();
    5. //入栈
    6. while(head != null) {
    7. stack.push(head);
    8. head = head.next;
    9. }
    10. if(stack.isEmpty()) {
    11. return null;
    12. }
    13. //出栈
    14. ListNode node = stack.pop();
    15. ListNode newHead = node;
    16. while(!stack.isEmpty()) {
    17. ListNode tmpNode = stack.pop();
    18. //建链表
    19. node.next = tmpNode;
    20. node = node.next;
    21. }
    22. //走到这里栈空 新链表建成
    23. node.next = null;
    24. return newHead;
    25. }
    26. }

    2.链表内指定区间反转

    题目链接 链表内指定区间反转_牛客题霸_牛客网 (nowcoder.com)

    题目要求

     题目分析

    上代码(保存结点位置,切断链表,反转链表,再连接)

    1. public class Solution {
    2. public ListNode reverseBetween (ListNode head, int m, int n) {
    3. //1.建立虚拟头结点
    4. ListNode temp = new ListNode(-1);
    5. temp.next = head;
    6. //2.记录n的前一个节点
    7. ListNode pro = temp;
    8. for(int i = 0; i < m-1; i++) {
    9. pro = pro.next;
    10. }
    11. //3.找到n位置下的结点
    12. ListNode nNode = pro;
    13. for(int i = 0; i < n-m+1; i++) {
    14. nNode = nNode.next;
    15. }
    16. //4.记录m位置下的结点,和n后下一个结点的位置
    17. ListNode mNode = pro.next;
    18. ListNode cur = nNode.next;
    19. //5.断开m到n之间的连接
    20. pro.next = null;
    21. nNode.next = null;
    22. //6.反转m到n之间的链表
    23. reverseList(mNode);
    24. //7.连接反转后的子链表
    25. pro.next = nNode;
    26. mNode.next = cur;
    27. return temp.next;
    28. }
    29. //反转链表
    30. public ListNode reverseList(ListNode head){
    31. //取一个新的头结点
    32. ListNode newHead = null;
    33. while(head != null) {
    34. ListNode cur = head.next;
    35. //头插法
    36. head.next = newHead;
    37. newHead = head;
    38. head = cur;
    39. }
    40. return newHead;
    41. }
    42. }

    上代码(穿针引线)

    1. import java.util.*;
    2. public class Solution {
    3. //3.穿针引线
    4. public ListNode reverseBetween (ListNode head, int m, int n) {
    5. //1.设置虚拟头结点
    6. ListNode dummyNode = new ListNode(-1);
    7. dummyNode.next = head;
    8. //2.记录m前的一个节点
    9. ListNode pro = dummyNode;
    10. for(int i = 0; i < m-1; i++) {
    11. pro = pro.next;
    12. }
    13. //3.记录待反转的第一个结点和这个节点的下一个节点的位置
    14. ListNode cur = pro.next;
    15. //4.开始穿针引线
    16. for(int i = 0; i < n-m; i++) {
    17. ListNode next = cur.next;
    18. cur.next = next.next;
    19. next.next = pro.next;
    20. pro.next = next;
    21. }
    22. return dummyNode.next;
    23. }
    24. }

    3. 链表中的节点每k个一组翻转

    题目链接 链表中的节点每k个一组翻转_牛客题霸_牛客网 (nowcoder.com)

    题目要求 

    题目分析

    (1)直接暴力解法,根据K给链表分组,然后子链表反转,再重新连接子链表

    上代码

    1. public class Solution {
    2. /**
    3. *
    4. * @param head ListNode类
    5. * @param k int整型
    6. * @return ListNode类
    7. */
    8. public ListNode reverseKGroup (ListNode head, int k) {
    9. //创建虚拟结点
    10. ListNode dummy = new ListNode(0);
    11. dummy.next = head;
    12. //创建两个节点,分别表示每个分组的前一个和后一个节点
    13. ListNode pre = dummy;
    14. ListNode end = dummy;
    15. while(end.next != null) {
    16. //每k个一组反转,【pre,end】
    17. for(int i = 0; i < k & end != null; i++) {
    18. end = end.next;
    19. }
    20. //如果end为空,说明不满一组
    21. if(end == null) {
    22. break;
    23. }
    24. //记录每组的头结点,和下一组的头结点
    25. ListNode start = pre.next;
    26. ListNode next = end.next;
    27. //断开连接,进行分组反转
    28. end.next = null;
    29. pre.next = reverseList(start);
    30. //反转之后重新连接
    31. start.next = next;
    32. //重新给pre和end赋值
    33. pre = start;
    34. end = start;
    35. }
    36. return dummy.next;
    37. }
    38. //反转链表
    39. private ListNode reverseList(ListNode head){
    40. ListNode newHead = null;
    41. while(head != null) {
    42. ListNode temp = head.next;
    43. head.next = newHead;
    44. newHead = head;
    45. head = temp;
    46. }
    47. return newHead;
    48. }
    49. }

    (2)也可以使用栈来做

    创建栈  先入栈,每次判断是否够k个节点,如果够,就出栈到新的链表中。如果不够就返回

    每次要把第k+1个结点记住,因为后面要把每次出完栈的子链表连接在一起

    1. public ListNode reverseKGroup (ListNode head, int k) {
    2. if(head == null) {
    3. return head;
    4. }
    5. //1.创建栈,创建虚拟头结点
    6. Stack stack = new Stack<>();
    7. ListNode dummy = new ListNode(-1);
    8. dummy.next = head;
    9. ListNode pre = dummy;
    10. int n = k;
    11. while(pre != null && pre.next != null) {
    12. ListNode temp = pre.next;
    13. //2.入栈
    14. while(temp != null && n > 0) {
    15. stack.add(temp);
    16. temp = temp.next;
    17. n--;
    18. }
    19. //3.记录第k+1个结点,用作连接
    20. ListNode nextNode = stack.peek().next;
    21. //4.n=0,说明栈中刚好k个结点,出栈连接
    22. if(n == 0) {
    23. while(!stack.empty()) {
    24. pre.next = stack.pop();
    25. pre = pre.next;
    26. }
    27. }else {
    28. break;
    29. }
    30. //5.连接
    31. pre.next = nextNode;
    32. n = k;
    33. }
    34. return dummy.next;
    35. }

    4. 合并两个排序的链表

    题目链接  合并两个排序的链表_牛客题霸_牛客网 (nowcoder.com)

    题目要求 

    题目分析

    (1)直接迭代

    1.创建一个虚拟头结点,然后再来一个节点prev指向虚拟头结点

    2.两个链表头结点先比较,小的结点,放在prev后面,然后这条链表节点向后走一位

    3.继续重复比较,每放一个节点prev向后走一位

    4.直到某一条链表为null,剩下的这个链表直接连在prev后面

    上代码

    1. public ListNode Merge(ListNode list1,ListNode list2) {
    2. ListNode newHead = new ListNode(-1);
    3. ListNode prev = newHead;
    4. while(list1 != null && list2 != null) {
    5. if(list1.val <= list2.val) {
    6. prev.next = list1;
    7. list1 = list1.next;
    8. } else {
    9. prev.next = list2;
    10. list2 = list2.next;
    11. }
    12. prev = prev.next;
    13. }
    14. //合并之后,谁还有剩余的就连在prev的后面
    15. prev.next = list1 == null ? list2:list1;
    16. return newHead.next;
    17. }

    方法二:递归   先分一下

    情况1.两条链表一条为空,另一条不为空,直接返回不为null的链表

    情况2.刚开始两条链表头结点比较,小的结点1的那条链表,就可以想象成确定了的(也可以想象成这个节点就是结果中的结点),然后继续往下递归比较,直到出现情况1的条件

    1. public ListNode Merge(ListNode list1,ListNode list2) {
    2. if(list1 == null) {
    3. return list2;
    4. }else if(list2 == null) {
    5. return list1;
    6. }else if(list1.val < list2.val) {
    7. list1.next = Merge(list1.next,list2);
    8. return list1;
    9. }else {
    10. list2.next = Merge(list1,list2.next);
    11. return list2;
    12. }
    13. }

    5. 合并k个已排序的链表 

    题目链接   合并k个已排序的链表_牛客题霸_牛客网 (nowcoder.com)

    题目要求

    题目分析

    (1)最暴力直接的解法就是,两个两个合并

    既然已经给了K个已经排好序的链表,那么for循环,让每两个链表合并之后,就和下一个合并

    两个链表合并,上一个题已经写过了,可以迭代也可以递归

    但是这样两个合并的方法,可能会超时(如果链表特别多的情况下),不建议使用

    上代码

    1. public ListNode mergeKLists(ArrayList lists) {
    2. ListNode ans = null;
    3. for(int i = 0; i < lists.size(); i++) {
    4. ans = mergeTwoList(ans,lists.get(i));
    5. }
    6. return ans;
    7. }
    8. private ListNode mergeTwoList(ListNode l1,ListNode l2){
    9. if(l1 == null || l2 == null) {
    10. return l1 == null ? l2:l1;
    11. }
    12. ListNode newHead = new ListNode(0);
    13. ListNode tmp = newHead;
    14. while(l1 != null && l2 != null) {
    15. if(l1.val <= l2.val) {
    16. tmp.next = l1;
    17. l1 = l1.next;
    18. }else {
    19. tmp.next = l2;
    20. l2 = l2.next;
    21. }
    22. tmp = tmp.next;
    23. }
    24. tmp.next = (l1 == null) ? l2:l1;
    25. return newHead.next;
    26. }

    (2)分治(归并排序)

    题中给的是ArrayList类型的链表数组

    定义left和right分别指向这个链表数组的左右两边进行分组,往下递归直到left==right

    然后开始合并,这就和上一题一样了

    1. public class Solution {
    2. //合并
    3. private ListNode Merge (ListNode list1,ListNode list2) {
    4. if(list1 == null) {
    5. return list2;
    6. }
    7. if(list2 == null) {
    8. return list1;
    9. }
    10. ListNode newHead = new ListNode(0);
    11. ListNode cur = newHead;
    12. //两个链表都不为null
    13. while(list1 != null && list2 != null) {
    14. //取出较小值的结点
    15. if(list1.val <= list2.val) {
    16. cur.next = list1;
    17. list1 = list1.next;
    18. }else {
    19. cur.next = list2;
    20. list2 = list2.next;
    21. }
    22. cur = cur.next;
    23. }
    24. cur.next = (list1 == null) ? list2 : list1;
    25. return newHead.next;
    26. }
    27. //划分区间
    28. private ListNode divideMerge(ArrayList lists,int left, int right) {
    29. if(left > right){
    30. return null;
    31. }else if(left == right) {
    32. return lists.get(left);
    33. }
    34. int mid = left + (right-1)/2;
    35. return Merge(divideMerge(lists,left,mid),divideMerge(lists,mid+1,right));
    36. }
    37. public ListNode mergeKLists(ArrayList lists) {
    38. //k个链表归并排序
    39. return divideMerge(lists,0,lists.size()-1);
    40. }
    41. }

    (3)使用优先级队列,本质上就是堆,内置的是小根堆

    1.创建一个优先级队列,并且还要重载比较方法,构造一个比较链表结点值的小根堆

    2.然后遍历每个链表的头,将不为null的结点加入的优先级队列中

    3.加一个虚拟头结点,将每次从堆顶取出来的元素,加入到虚拟头结点的后面

    4.每次从堆顶取出一个元素后,就要给堆中重新放入刚刚取出来的结点的下一个结点。并且还要判空

    1. //使用优先级队列
    2. public ListNode mergeKLists(ArrayList lists) {
    3. Queue pq = new PriorityQueue<>(
    4. (v1,v2) -> v1.val - v2.val);
    5. for(int i = 0; i < lists.size(); i++) {
    6. //不为null,就加入小根堆
    7. if(lists.get(i) != null) {
    8. pq.add(lists.get(i));
    9. }
    10. }
    11. //加一个虚拟头结点
    12. ListNode newHead = new ListNode(-1);
    13. ListNode cur = newHead;
    14. while(!pq.isEmpty()){
    15. //小根堆,取出就是最小的元素
    16. ListNode temp = pq.poll();
    17. //连接
    18. cur.next = temp;
    19. cur = cur.next;
    20. //每次取出堆顶元素后,再放入链表的下一个元素进入小根堆
    21. if(temp.next != null) {
    22. pq.add(temp.next);
    23. }
    24. }
    25. return newHead.next;
    26. }

  • 相关阅读:
    HTTP状态码
    MySQL 数据库 定义参数【连接查询】
    60、Flink 的异步 IO 算子使用异步 Http 客户端查高德地图
    工厂模式 与 抽象工厂模式 的区别
    基于JAVA中学网站设计与实现演示录像2020计算机毕业设计源码+系统+数据库+lw文档+部署
    linux:数据库连接
    Vue项目 配置项设置
    Q3营收同比翻三倍,踩猛“油门”零跑必将领跑?
    zookeeper实现动态上下线
    ShardingSphere-JDBC实战
  • 原文地址:https://blog.csdn.net/m0_58761900/article/details/126165151