• 【数据结构与算法】链表的实现以及相关算法


    目录

    双链表的实现

    单链表的实现

    有序列表的合并(双指针法)

    链表的反转 

    链表实现两数之和 

    判定链表是否有环

    判断链表是否相交

    单链表的排序


    双链表的实现

    1. public class DLinkedList {
    2. private Node first;
    3. private Node last;
    4. int size;
    5. /**
    6. * 头插法
    7. * @param item
    8. */
    9. public void addFirst(E item){
    10. Node f =first;
    11. Node newNode =new Node(null,item,f);
    12. first =newNode;
    13. if (f==null){
    14. last =newNode;
    15. }else {
    16. f.prev=newNode;
    17. }
    18. size++;
    19. }
    20. /**
    21. * 尾插法
    22. * @param item
    23. */
    24. public void addLast(E item){
    25. Node l =last;
    26. Node newNode =new Node(l,item,null);
    27. last =newNode;
    28. if (l==null){
    29. first=newNode;
    30. }else {
    31. l.next=newNode;
    32. }
    33. size++;
    34. }
    35. /**
    36. * 删除头节点
    37. */
    38. public void deleteFirst(){
    39. Node f =first;
    40. Node n =first.next;
    41. f.next =null;
    42. f.item=null;
    43. first=n;
    44. if (n==null){
    45. last=null;
    46. }else {
    47. n.prev=null;
    48. }
    49. size--;
    50. }
    51. /**
    52. * 删除尾节点
    53. */
    54. public void deleteLast(){
    55. Node l =last;
    56. Node p =last.prev;
    57. l.item=null;
    58. l.prev=null;
    59. last=p;
    60. if (p==null){
    61. first=null;
    62. }else {
    63. p.next=null;
    64. }
    65. size--;
    66. }
    67. /**
    68. * 从头开始遍历
    69. * @return
    70. */
    71. public String toStringForFirst() {
    72. StringJoiner sj =new StringJoiner("->");
    73. for (Node n= first;n!=null;n =n.next){
    74. sj.add(n.item.toString());
    75. }
    76. return sj.toString();
    77. }
    78. /**
    79. * 从尾开始遍历
    80. * @return
    81. */
    82. public String toStringForLast(){
    83. StringJoiner sj =new StringJoiner("<-");
    84. for (Node n =last;n!=null;n=n.prev){
    85. sj.add(n.item.toString());
    86. }
    87. return sj.toString();
    88. }
    89. @Override
    90. public String toString() {
    91. StringJoiner sj =new StringJoiner("<->");
    92. for (Node n= first;n!=null;n =n.next){
    93. sj.add(n.item.toString());
    94. }
    95. return sj.toString();
    96. }
    97. /**
    98. * 节点内部类
    99. * @param
    100. */
    101. static class Node{
    102. private E item;
    103. private Node prev;
    104. private Node next;
    105. Node(Node prev,E item,Node next){
    106. this.prev=prev;
    107. this.item=item;
    108. this.next=next;
    109. }
    110. }
    111. }

    单链表的实现

    1. public class LinkedList1 {
    2. //头节点
    3. Node first;
    4. //尾节点
    5. Node last;
    6. //大小
    7. int size = 0;
    8. //头插法
    9. public void addFirst(int v) {
    10. Node newNode = new Node(v);
    11. Node f = first;
    12. first = newNode;
    13. if (f == null) {
    14. last = newNode;
    15. } else {
    16. newNode.next = f;
    17. }
    18. size++;
    19. }
    20. //尾插法
    21. public void addLast(int v) {
    22. Node newNode = new Node(v);
    23. Node l = last;
    24. last = newNode;
    25. if (l == null) {
    26. first = newNode;
    27. } else {
    28. l.next = newNode;
    29. }
    30. size++;
    31. }
    32. //使用节点尾插
    33. public void addNode(Node node){
    34. if (last ==null){
    35. last=node;
    36. first=node;
    37. }else {
    38. Node l =last;
    39. l.next=node;
    40. last=node;
    41. }
    42. }
    43. //链表的遍历
    44. @Override
    45. public String toString() {
    46. StringJoiner sj = new StringJoiner("->");
    47. for (Node n = first; n != null; n = n.next) {
    48. sj.add(String.valueOf(n.val));
    49. }
    50. return sj.toString();
    51. }
    52. public static class Node {
    53. int val;
    54. Node next;
    55. Node(int val) {
    56. this.val = val;
    57. }
    58. }
    59. }

    以下所有算法都是基于上面单链表的实现方法

    有序列表的合并(双指针法)

    1. //链表的有序合并排序
    2. public static LinkedList1 merge(LinkedList1 list1, LinkedList1 list2) {
    3. Node n1 = list1.first, n2 = list2.first;
    4. LinkedList1 result = new LinkedList1();
    5. while (n1 != null || n2 != null) {
    6. if (n1 == null) {
    7. result.addLast(n2.val);
    8. n2 = n2.next;
    9. continue;
    10. }
    11. if (n2 == null) {
    12. result.addLast(n1.val);
    13. n1 = n1.next;
    14. continue;
    15. }
    16. if (n1.val < n2.val) {
    17. result.addLast(n1.val);
    18. n1 = n1.next;
    19. } else {
    20. result.addLast(n2.val);
    21. n2 = n2.next;
    22. }
    23. }
    24. return result;
    25. }

     测试

    1. LinkedList1 linkedList1 =new LinkedList1();
    2. linkedList1.addLast(1);
    3. linkedList1.addLast(4);
    4. linkedList1.addLast(6);
    5. LinkedList1 linkedList2 =new LinkedList1();
    6. linkedList2.addLast(2);
    7. linkedList2.addLast(3);
    8. linkedList2.addLast(5);
    9. linkedList2.addLast(9);
    10. System.out.println("链表1:"+linkedList1);
    11. System.out.println("链表2:"+linkedList2);
    12. //有序链表的合并排序
    13. System.out.println("链表1与链表2合并:"+LinkedList1.merge(linkedList1, linkedList2));

    链表的反转 

    1. //链表的反转
    2. public static LinkedList1 reverseLinked(LinkedList1 list1) {
    3. Stack stack = new Stack<>();
    4. for (Node n = list1.first; n != null; n = n.next) {
    5. stack.add(n);
    6. }
    7. LinkedList1 result = new LinkedList1();
    8. while (!stack.isEmpty()) {
    9. result.addLast(stack.pop().val);
    10. }
    11. return result;
    12. }

    测试

    1. LinkedList1 linkedList1 =new LinkedList1();
    2. linkedList1.addLast(1);
    3. linkedList1.addLast(4);
    4. linkedList1.addLast(6);
    5. System.out.println("链表1:"+linkedList1);
    6. //链表的反转
    7. System.out.println("链表1反转后:"+LinkedList1.reverseLinked(linkedList1));

    测试结果

    链表实现两数之和 

    1. //两数之和
    2. public static LinkedList1 addNumber(LinkedList1 list1, LinkedList1 list2) {
    3. Node n1 = list1.first, n2 = list2.first;
    4. LinkedList1 result = new LinkedList1();
    5. int carry = 0;
    6. while (n1 != null || n2 != null) {
    7. int x = n1 != null ? n1.val : 0;
    8. int y = n2 != null ? n2.val : 0;
    9. int sum = x + y + carry;
    10. carry = sum / 10;
    11. result.addLast(sum % 10);
    12. if (n1 != null) {
    13. n1 = n1.next;
    14. }
    15. if (n2 != null) {
    16. n2 = n2.next;
    17. }
    18. }
    19. if (carry != 0) {
    20. result.addLast(carry);
    21. }
    22. return result;
    23. }

    测试

    1. LinkedList1 linkedList1 =new LinkedList1();
    2. linkedList1.addLast(1);
    3. linkedList1.addLast(4);
    4. linkedList1.addLast(6);
    5. LinkedList1 linkedList2 =new LinkedList1();
    6. linkedList2.addLast(2);
    7. linkedList2.addLast(3);
    8. linkedList2.addLast(5);
    9. linkedList2.addLast(9);
    10. //两数之和
    11. System.out.println("链表1:"+linkedList1);
    12. System.out.println("链表2:"+linkedList2);
    13. System.out.println("链表1+链表2:"+LinkedList1.addNumber(linkedList1,linkedList2));

    测试结果(注意从左到右依次是个十百位)

    判定链表是否有环

    方法一 通过set集合

    1. //set集合判断是否有环
    2. public static boolean hasCycle(Node node) {
    3. Set set = new HashSet<>();
    4. while (node != null) {
    5. if (set.contains(node)) {
    6. return true;
    7. }
    8. set.add(node);
    9. node = node.next;
    10. }
    11. return false;
    12. }

    方法二 通过快慢指针

    1. //快慢指针判断是否有环
    2. public static boolean hasCycle2(Node node) {
    3. Node fast = node;//快指针
    4. Node slow = node;//慢指针
    5. //判断是不是空节点
    6. if (node == null) {
    7. return false;
    8. }
    9. while (fast != null && fast.next != null && slow != null) {
    10. fast = fast.next.next;
    11. slow = slow.next;
    12. if (fast == slow) {
    13. return true;
    14. }
    15. }
    16. return false;
    17. }

    测试 无论方法一还是二 测试结果都是相同 一般使用方法二 效率更高 更节省资源

    1. LinkedList1.Node node1 =new LinkedList1.Node(1);
    2. LinkedList1.Node node2 =new LinkedList1.Node(2);
    3. LinkedList1.Node node3 =new LinkedList1.Node(3);
    4. LinkedList1.Node node4 =new LinkedList1.Node(4);
    5. LinkedList1.Node node5 =new LinkedList1.Node(5);
    6. node1.next=node2;
    7. node2.next=node3;
    8. node3.next=node4;
    9. node4.next=node5;
    10. node5.next=node3;//环 注意这是专门设置的环
    11. //set集合判断是否有环
    12. System.out.println("set集合判断是否有环:"+LinkedList1.hasCycle(node1));
    13. //快慢指针判断是否有环
    14. System.out.println("快慢指针判断是否有环:"+LinkedList1.hasCycle2(node1));

    测试结果

    判断链表是否相交

    方法一  双重循环暴力解决两条链表是否相交

    1. //双重循环暴力解决两条链表是否相交
    2. public static boolean isCross(LinkedList1 list1,LinkedList1 list2){
    3. for (Node n1 =list1.first;n1!=null;n1=n1.next){
    4. for (Node n2 =list2.first;n2!=null;n2=n2.next){
    5. if (n1==n2){
    6. return true;
    7. }
    8. }
    9. }
    10. return false;
    11. }

    方法二 双指针判断链表是否相交

    1. //双指针判断链表是否相交
    2. public static boolean isCross2(LinkedList1 list1,LinkedList1 list2){
    3. if (list1 ==null ||list2==null){
    4. return false;
    5. }
    6. Node p =list1.size()>list2.size()?list1.first:list2.first;
    7. Node q =list1.size()>list2.size()?list2.first:list1.first;
    8. int diff =Math.abs(list1.size()-list2.size());
    9. while (diff-- >0){
    10. p=p.next;
    11. }
    12. while (p!=q){
    13. p=p.next;
    14. q=q.next;
    15. }
    16. if (p!=null){
    17. return true;
    18. }else {
    19. return false;
    20. }
    21. }

    测试

    1. LinkedList1.Node node1 =new LinkedList1.Node(1);
    2. LinkedList1.Node node2 =new LinkedList1.Node(2);
    3. LinkedList1.Node node3 =new LinkedList1.Node(3);
    4. LinkedList1.Node node4 =new LinkedList1.Node(4);
    5. LinkedList1.Node node5 =new LinkedList1.Node(5);
    6. LinkedList1.Node nodeA =new LinkedList1.Node('A');
    7. LinkedList1.Node nodeB =new LinkedList1.Node('B');
    8. LinkedList1.Node nodeC =new LinkedList1.Node('C');
    9. LinkedList1 linkedList1 =new LinkedList1();
    10. linkedList1.addNode(node1);
    11. linkedList1.addNode(node2);
    12. linkedList1.addNode(node3);
    13. linkedList1.addNode(node4);
    14. linkedList1.addNode(node5);
    15. LinkedList1 linkedList2 =new LinkedList1();
    16. linkedList2.addNode(nodeA);
    17. linkedList2.addNode(nodeB);
    18. linkedList2.addNode(nodeC);
    19. linkedList2.addNode(node3);
    20. System.out.println(linkedList1);
    21. System.out.println(linkedList2);
    22. System.out.println(LinkedList1.isCross(linkedList1,linkedList2));
    23. System.out.println(LinkedList1.isCross2(linkedList1,linkedList2));

    测试结果

    单链表的排序

    链表的排序与数组是一样的这里使用冒泡排序

    1. /**
    2. * 冒泡排序
    3. * @param first 头节点
    4. */
    5. public void sortInList(Node first){
    6. if (first ==null||first.next==null){
    7. return ;
    8. }
    9. Node p,q;
    10. for (p=first;p!=null;p=p.next){
    11. for (q=p.next;q!=null;q=q.next){
    12. if (p.val>q.val){
    13. int temp =p.val;
    14. p.val=q.val;
    15. q.val=temp;
    16. }
    17. }
    18. }
    19. }

    测试

    1. LinkedList1 linkedList2 = new LinkedList1();
    2. linkedList2.addLast(5);
    3. linkedList2.addLast(3);
    4. linkedList2.addLast(2);
    5. linkedList2.addLast(9);
    6. System.out.println(linkedList2);
    7. linkedList2.sortInList(linkedList2.first);
    8. System.out.println(linkedList2);

    测试结果

  • 相关阅读:
    学生HTML个人网页作业作品 简单的IT技术个人简历模板html下载 简单个人网页设计作业 静态HTML个人博客主页
    java 多线程乐观锁与悲观锁
    【Leetcode刷题】二分查找
    VS Code | 在VS Code中搭建你的R语言运行环境吧!~(图文介绍超详细)
    【Prism系列】Prism中的命令
    FFmpeg开发笔记(一)搭建Linux系统的开发环境
    【LeetCode】插入区间
    探究Nginx应用场景
    使用显著性检测的可见光和红外图像的两尺度图像融合(Matlab代码实现)
    机器学习中的集成学习算法和K-近邻算法及其优缺点
  • 原文地址:https://blog.csdn.net/c1390527393/article/details/133325729