• 单链表和双链表


    单链表和双链表

    单链表:只有一个指向下一节点的指针   -->  单向读取

    双链表:既有指向下一节点的指针,也有指向上一节点的指针,可以通过此向前查找

    单链表和双链表的反转:逆序

    整个链表逆序、部分链表逆序

    是否需要返回值

    链表的反转需要换头(整个链表逆序)的操作,则需要返回值(需要一个Node类型的返回值,存储换头之后的head值)

    单链表的逆序及部分链表的逆序

    注:当head为整个链表的头结点时,pre初始值为null,next指针反转之后head头节点指向pre,也就是null 

    1. package linkedlist;
    2. public class SingleLinkedList {
    3. class Node {
    4. public int value;
    5. public Node next;
    6. public Node(int data) {
    7. this.value = data;
    8. }
    9. }
    10. //单链表的反转
    11. //单链表的反转只需要将指针的方向反转,在逆序整个链表的时候,用head遍历链表的所有结点,使得每个指针由后一结点指向前一结点
    12. public static Node reverseList(Node head) {//head传入时是链表的头结点,在循环中为当前结点
    13. Node pre = null;//处理的结点的前一个结点
    14. Node next = null;//处理的结点的后一个结点
    15. while (head != null) {
    16. next = head.next;//head的后一个结点
    17. head.next = pre;//反转指针
    18. pre = head;
    19. head = next;
    20. }
    21. return pre;//出循环时head==null,pre为整个链表的最后一个结点,也就是链表反转之后的头
    22. }
    23. //部分单链表的反转
    24. //这里说部分反转应当是不需要返回值的,但是取的不同的startend数值,可能会导致链表的头改变,需要返回值,因此返回
    25. public static Node reverselistPart(Node head, int start, int end) {//head为当前结点,starthe end为起始位置
    26. Node startNode = null;
    27. Node endNode = null;
    28. int length = 0;//因为链表与数组不同,不能够直到当前结点在整个链表之间的位置,需要计数
    29. Node pre = head;
    30. //遍历整个链表,此时pre指的是当前结点
    31. while (pre != null) {//获得整个链表的长度
    32. length++;
    33. //找到startNode节点的前一个结点
    34. if (length == start - 1) {
    35. startNode = pre;
    36. }
    37. //找到endNode节点的后一个节点
    38. if (length == end + 1) {
    39. endNode = pre;
    40. }
    41. pre = pre.next;//遍历,避免死循环
    42. }
    43. //判断所给的startend是否合理
    44. if (start < 1 || end > length || start >= end) {
    45. return head;//不合理的数视为不对链表进行逆序
    46. } else if (startNode == null) {//start==1,反转的部分包含头结点
    47. pre = head;
    48. } else {
    49. pre = startNode.next;
    50. }
    51. //反转startNode和后一个结点
    52. //pre为开始结点的下一个结点,headNode为pre结点的下一个结点
    53. Node headNode = pre.next;
    54. pre.next = startNode;//next指针由pre指向startNode,实现指针的反转
    55. Node next = null;
    56. //反转从startNode后一个结点到endNode的前一个结点中所有的结点
    57. while (headNode != endNode) {//链表的反转
    58. next = headNode.next;
    59. headNode.next = pre;
    60. pre = headNode;
    61. headNode = next;
    62. }
    63. // if (startNode != null) {
    64. // startNode.next = pre;
    65. // return headNode;
    66. // }
    67. return pre;
    68. }
    69. }

    双链表的逆序

    双链表的逆序和单链表相同,但是需要反转pre和next两个指针

    1. package linkedlist;
    2. class DoubleNode {
    3. public DoubleNode(int data) {
    4. this.value = data;
    5. }
    6. public int value;
    7. public DoubleNode next;
    8. public DoubleNode pre;
    9. public static DoubleNode reverseList(DoubleNode head) {
    10. DoubleNode pre = null;
    11. DoubleNode next = null;
    12. while (head != null) {
    13. next = head.next;
    14. head.next = pre;
    15. head.pre = next;
    16. pre = head;
    17. head = next;
    18. }
    19. return pre;
    20. }
    21. }

    打印两个有序链表的公共部分

    判断一个链表是否是一个回文结构

    1、将链表结构放到栈中,比较栈弹出的顺序是否与原序相同(栈先进后出),全部相同则是回文

          O(n)的额外空间

    1. package linkedlist;
    2. import java.util.Stack;
    3. public class IsPalindromic {
    4. class Node {
    5. public int value;
    6. public Node next;
    7. public Node(int data) {
    8. this.value = data;
    9. }
    10. }
    11. public static boolean isplindromicbyStack(Node head) {
    12. Stack<Node> stack = new Stack<Node>();//创建栈结构
    13. Node temp = head;
    14. while (temp != null) {
    15. stack.push(temp);//入栈
    16. temp = temp.next;
    17. }
    18. while (head != null){
    19. if(head.value != stack.pop().value){//出栈,判断前后顺序是否相同
    20. return false;
    21. }
    22. head = head.next;
    23. }
    24. return true;
    25. }
    26. }

    2、优化:降低空间复杂度

          O(n/2)的额外空间

    将右半部分的链表放到栈中,从链表左侧开始遍历,每遍历一个栈弹出一个,一一进行比较,全部相同则回文       

            单链表无法获知整个链表有多少数,单指针无法判断此时经历的结点是否是链表结构的一半

            快慢指针:快指针一次走2步,慢指针一次走1步;当快指针遍历完整个链表,慢指针走到中点

            在实际情况中快慢指针需要根据题目调整代码

    1. public static boolean isplindromicbyfastandslowPointer(Node head){
    2. if(head == null || head.next == null){
    3. return true;//整个链表为空或是只有一个Node也被视为回文结构
    4. }
    5. //快慢指针
    6. Node slow = head.next;
    7. Node fast = head;
    8. //整个链表为偶数时,慢指针位于n/2位置上;奇数时,整个链表位于n/2位置向上取整+15 - 4
    9. while(fast.next != null && fast.next.next != null){
    10. slow = slow.next;
    11. fast = fast.next.next;
    12. }
    13. //取出后半段入栈
    14. Stack<Node> stack = new Stack<Node>();
    15. while(slow != null){
    16. stack.push(slow);
    17. slow = slow.next;
    18. }
    19. //将后半段与前半段比较,是否相同
    20. while(!stack.isEmpty()){
    21. if(head.value != stack.pop().value){
    22. return false;
    23. }
    24. head = head.next;
    25. }
    26. return true;
    27. }

    3、优化:不使用额外的数据结构

          O(1)的额外空间

    快指针一次走2步,慢指针一次走1步;慢指针走到中间的位置,遍历后面部分的链表逆序;

    两个指针,一个从head向后遍历,一个从end向前遍历,直到一个指针指向空

    1. public static boolean isplindromicbytwoPointer(Node head) {
    2. if (head == null || head.next == null) {
    3. return true;//整个链表为空或是只有一个Node也被视为回文结构
    4. }
    5. Node slow = head;
    6. Node fast = head;
    7. while (fast.next != null && fast.next.next != null) {
    8. slow = slow.next;//最后到达中点位置,奇数时中点位置向上取整
    9. fast = fast.next.next;//最后到达链表最后一位或两位
    10. }
    11. fast = slow.next;//从右半部分的起点开始
    12. slow.next = null;//空出空间便于后半部分逆序
    13. //后半部分逆序
    14. Node temp = null;
    15. while (fast != null) {
    16. temp = fast.next;
    17. fast.next = slow;//反转指针
    18. slow = fast;
    19. fast = temp;
    20. }
    21. temp = slow;//slow为反转部分的最后一个结点,也就是后半部分反转部分的头
    22. fast = head;//左半部分的头
    23. boolean res = true;
    24. //slow表示右半部分,fast表示左半部分
    25. while (slow != null && fast != null) {
    26. if (slow.value != fast.value) {
    27. res = false;
    28. break;
    29. }
    30. slow = slow.next;
    31. fast = fast.next;
    32. }
    33. //将链表反转回去 slow -- fast , temp -- slow , fast -- temp
    34. slow = temp.next;
    35. temp.next = null;
    36. // fast = slow.next;
    37. // slow.next = null;
    38. while (slow != null) {
    39. // while (fast != null)
    40. fast = slow.next;
    41. slow.next = temp;
    42. temp = slow;
    43. slow = fast;
    44. // temp = fast.next;
    45. // fast.next = slow;//反转指针
    46. // slow = fast;
    47. // fast = temp;
    48. }
    49. return res;
    50. }

    将单链表按某值划分为左边小、中间相等、右边大的形式

    使用额外空间:将单链表的每个结点放到数组中,对数组进行partition操作(左边小、中间相等、右边大) 

    不使用额外空间:六个变量,分别为小于、等于、大于部分的头和尾

    注:三区域串起来的时候需要注意区域是否存在,否则会指向null导致出错 

    1. package linkedlist;
    2. public class ListPartition {
    3. class Node {
    4. public int value;
    5. public Node next;
    6. public Node(int data) {
    7. this.value = data;
    8. }
    9. }
    10. public static Node listPattition(Node head, int pivot) {
    11. Node SH = null;
    12. Node ST = null;
    13. Node EH = null;
    14. Node ET = null;
    15. Node BH = null;
    16. Node BT = null;
    17. Node temp = null;
    18. while (head != null) {
    19. temp = head.next;//存储下一个head
    20. head.next = null;//消去指针
    21. if (head.value < pivot) {
    22. if (SH == null) {
    23. SH = head;
    24. ST = head;
    25. } else {
    26. ST.next = head;
    27. ST = head;
    28. }
    29. }
    30. if (head.value == pivot) {
    31. if (EH == null) {
    32. EH = head;
    33. ET = head;
    34. } else {
    35. ET.next = head;
    36. ET = head;
    37. }
    38. }
    39. if (head.value > pivot) {
    40. if (BH == null) {
    41. BH = head;
    42. BT = null;
    43. } else {
    44. BT.next = head;
    45. BT = head;
    46. }
    47. }
    48. head = temp;//再次进行赋值,使得head遍历整个链表
    49. }
    50. if (ST != null) {//有小于区域
    51. if (ET != null) {//有等于区域
    52. ST.next = EH;
    53. } else if (BT != null) {//没有等于区域,有大于区域
    54. ST.next = BH;
    55. } else {//啥也没有
    56. ST.next = null;
    57. }
    58. }
    59. if (ET != null) {//有等于区域
    60. if (BT != null) {//有大于区域
    61. ET.next = BH;
    62. } else {//无大于区域
    63. ET.next = null;
    64. }
    65. }
    66. //最终返回结果
    67. if(ST != null){
    68. return SH;
    69. } else if (ET != null) {
    70. return EH;
    71. }else{
    72. return BH;
    73. }
    74. }
    75. }

    复制含有随机指针节点的链表

    使用额外空间:哈希表拷贝节点,通过原来的节点1的next指针找到节点2,拷贝后的节点1'对应节点2',由此可知节点1'的next指针指向节点2'。random指针同理。

    不使用额外空间:在链表的每个节点后插入其对应拷贝节点,通过节点1的next指针找到拷贝的节点1',节点1的random指针指向节点3,节点3的next指针指向节点3',由此可以拷贝节点1'指向节点3'的random指针

    由next指针向下遍历,将每一对的指针都拷贝出,拷贝所有的random指针后抽离出拷贝的链表(画的好丑)

    1. package linkedlist;
    2. public class CopyLinkedListWithRand {
    3. class Node {
    4. public int value;
    5. public Node next;
    6. public Node rand;
    7. public Node(int data) {
    8. this.value = data;
    9. }
    10. }
    11. public Node copylistwithRand(Node head) {
    12. if (head == null) {
    13. return null;
    14. }
    15. Node node1 = head;
    16. Node node2 = null;
    17. //1 -> 2
    18. //1 -> 1' -> 2
    19. while (node1 != null) {
    20. node2 = node1.next;
    21. node1.next = new Node(node1.value);//克隆的新节点
    22. node1.next.next = node2;
    23. node1 = node2;
    24. }
    25. node1 = head;//重新来过
    26. //拷贝random指针
    27. while (node1 != null) {
    28. if (node1.rand != null) {//节点1的random指针
    29. node1.next.rand = node1.rand.next;//节点1'的random指针=节点3的next=节点3'
    30. } else {
    31. node1.next.rand = null;
    32. }
    33. node1 = node1.next.next;
    34. }
    35. Node res = head.next;//拷贝出来的链表的头节点
    36. node1 = head;//再次重新来过
    37. Node temp = res;
    38. while(node1 != null){
    39. node1 = node1.next.next;//跳到第三个,第五个...
    40. if(node1.next != null) {
    41. temp.next = node1.next;//第二个指向第四个,第四个指向第六个...
    42. }else{
    43. temp.next = null;
    44. }
    45. temp = temp.next;//第二个跳第四个,第四个跳第六个...
    46. }
    47. return res;//返回拷贝链表的头
    48. }
    49. }

  • 相关阅读:
    [笔记]攻防工具分享之 CobaltStrike框架 《二》生成后门
    DTS搭载全新自研内核,突破两地三中心架构的关键技术|腾讯云数据库
    自主品牌首次「赶超」合资,中国供应商「突围」智能电动赛道
    Qt的WebEngineView加载网页时出现Error: WebGL is not supported
    云安全-云服务器(RAM)后渗透
    56.【全局变量和局部变量专题】
    034:vue项目利用qrcodejs2生成二维码示例
    基于SSM的图书智能推荐销售系统
    Qt入门(零)——Qt概述
    大数据学习之Spark基础
  • 原文地址:https://blog.csdn.net/m0_73530538/article/details/132661280