链表

来源:互联网 发布:上海张江租房 知乎 编辑:程序博客网 时间:2024/05/08 23:40

1.逆序打印LinkedList

1.从尾到头打印list的话,考虑用stack先把各节点保存起来,遍历完以后再将stack弹出打印即可

2.用递归模拟stack,每次遍历list当前节点的时候,递归调用下一个节点,直到list结尾

   1: public void printReverseList(Node head) {
   2:     if (head == null)
   3:         return;
   4:     Node temp = head;
   5:     if (temp.getNext() != null) {
   6:         printReverseList(temp.getNext());
   7:     }
   8:     System.out.println(temp.getData());
   9: }


2.在O(1)时间内删除链表中指定的节点

已给出了要删除node的指针,可以很方便地找到要删除node的下一个node,将下一个node的信息复制到当前node,再将下一个node删除,这正好相当于将当前node删除!时间复杂度是O(1),需要注意的是假设当前node是最后一个的话node->next为空,上面的思路就不行了,我们还是需要找到尾节点的上一个节点!如果所有链表只有一个节点,那么我们将head置空即可。

   1: public void deleteNode(Node head, Node deleteNode) {
   2:     if (head == null || deleteNode == null) {
   3:         return;
   4:     }
   5:     Node nextNode;
   6:     if (deleteNode.getNext() != null) {
   7:         nextNode = deleteNode.getNext();
   8:         deleteNode.setDate(nextNode.getData());
   9:         deleteNode.setNext(nextNode.getNext());
  10:         nextNode = null;
  11:     } else if (deleteNode == head) {// 只有一个节点
  12:         head = null;
  13:     } else {// 删除尾节点,只能遍历一次
  14:         nextNode = head;
  15:         while (nextNode.getNext() != deleteNode)
  16:             nextNode = nextNode.getNext();
  17:         nextNode.setNext(null);
  18:         deleteNode = null;
  19:     }
  20: }


3.寻找list中倒数第k个节点

思路:使用两个指针,刚开始同时指向list头,然后第二个指针p2前进k-1步,然后第一个指针p1和p2,同时前进,知道p2到达list尾,这时p1指向倒数第k个节点(如果是找中间节点,则一个每次移一步,一个每次移两步)

   1: public Node find_k_reverseNode(Node head, int k) {
   2:     if (head == null || k < 1)
   3:         return null;
   4:     Node p1, p2;
   5:     p1 = p2 = head;
   6:     int i = 0;
   7:     while (p2.getNext() != null && (++i != k)) {
   8:         p2 = p2.getNext();
   9:     }
  10:     if (p2.getNext() == null) {// list长度小于k-1
  11:         return null;
  12:     }
  13:     while (p2.getNext() != null) {
  14:         p1 = p1.getNext();
  15:         p2 = p2.getNext();
  16:     }
  17:     return p1;
  18: }


4.反转list

最好画画图啥的,需要注意的中间需要一些节点保存指针,要不会造成list的断裂,需要三个指针,当前指针,当前指针前一个指针,当前指针后一个指针

   1: public Node reverseList(Node head) {
   2:     if (head == null || head.getNext() == null) {
   3:         return head;
   4:     }
   5:     // 一共需要三个指针,当前节点之前,当前节点,当前节点之后的节点。
   6:     // 这样才不会造成 调整过程中的list的断裂
   7:     Node preNode = null;
   8:     Node curNode = head;
   9:     Node nextNode = curNode.getNext();
  10:     while (nextNode != null) {
  11:         curNode.setNext(preNode);
  12:         preNode = curNode;
  13:         curNode = nextNode;
  14:         nextNode = nextNode.getNext();
  15:     }
  16:     curNode.setNext(preNode);// 这个时候curNode就是原来的尾节点
  17:     return curNode;
  18: }

递归方式:

   1: public Node reverseList(Node head) {
   2:     if (head == null) {
   3:         return null;
   4:     }
   5:     if (head.getNext() == null) {
   6:         return head;
   7:     }
   8:     Node node = reverseList(head.getNext());//先反转后面的链表  
   9:     head.getNext().setNext(head);//再将当前节点设置为其后面节点的后续节点  
  10:     head.setNext(null);
  11:     return node;
  12: }

 


5.合并两个有序list

   1: 递归方式:
   2:     public Node mergeList(Node p1, Node p2) {
   3:         if (p1 == null || p2 == null) {
   4:             return p1 == null ? p2 : p1;
   5:         }
   6:         Node result;
   7:         if (p1.getData() < p2.getData()) {
   8:             result = p1;
   9:             result.setpNext(mergeList(p1.getpNext(), p2));
  10:         } else {
  11:             result = p2;
  12:             result.setpNext(mergeList(p1, p2.getpNext()));
  13:         }
  14:         return result;
  15:     }
  16:  
  17:  
  18: 非递归:
  19:     public Node mergeList(Node p1, Node p2) {
  20:         if (p1 == null || p2 == null) {
  21:             return p1 == null ? p2 : p1;
  22:         }
  23:         Node result, current;
  24:         if (p1.getData() < p2.getData()) {
  25:             result = p1;
  26:             p1 = p1.getpNext();
  27:         } else {
  28:             result = p2;
  29:             p2 = p2.getpNext();
  30:         }
  31:         current = result;
  32:         while (p1 != null && p2 != null) {
  33:             if (p1.getData() < p2.getData()) {
  34:                 current.setpNext(p1);
  35:                 p1 = p1.getpNext();
  36:                 current = current.getpNext();
  37:             } else {
  38:                 current.setpNext(p2);
  39:                 p2 = p2.getpNext();
  40:                 current = current.getpNext();
  41:             }
  42:         }
  43:         if (p1 != null) {
  44:             current.setpNext(p1);
  45:         }else {
  46:             current.setpNext(p2);
  47:         }
  48:         return result;
  49:     }
  50:  

 


6. 判断俩个链表是否相交

给出俩个单向链表的头指针,比如 h1,h2,判断这俩个链表是否相交。为了简化问题,我们假设俩个链表均不带环。

问题扩展:

1.如果链表可能有环列?

2.如果需要求出俩个链表相交的第一个节点列?

   1: public Node isJoined(Node p1, Node p2) {
   2:     if (p1 == null || p2 == null) {
   3:         return null;
   4:     }
   5:     Node cylic1 = hasCylic(p1);
   6:     Node cylic2 = hasCylic(p2);
   7:     if (cylic1 == null && cylic2 == null) {// 都无环
   8:         Node h1 = p1, h2 = p2;
   9:         int i = 1, k = 1;
  10:         while (h1.getpNext() != null) {
  11:             i++;
  12:             h1 = h1.getpNext();
  13:         }
  14:         while (h2.getpNext() != null) {
  15:             k++;
  16:             h2 = h2.getpNext();
  17:         }
  18:         if (h1 != h2) {// 无交点
  19:             return null;
  20:         }
  21:         // 有交点
  22:         h1 = p1;
  23:         h2 = p2;
  24:         if (i >= k) {// p1链比p2链长
  25:             for (int j = 0; j < i - k; j++) {
  26:                 h1 = h1.getpNext();
  27:             }
  28:         } else {// p2链比p1链长
  29:             for (int j = 0; j < i - k; j++) {
  30:                 h2 = h2.getpNext();
  31:             }
  32:         }
  33:         while (h1 != h2) {
  34:             h1 = h1.getpNext();
  35:             h2 = h2.getpNext();
  36:         }
  37:         return h1;
  38:     } else if ((cylic1 == null && cylic2 != null)
  39:             || (cylic1 != null && cylic2 == null)) {// 一个有环,一个无环
  40:         return null;
  41:     }
  42:     // 都有环
  43:     Node p = cylic1.getpNext();
  44:     Node q = cylic2.getpNext();
  45:     if (p == cylic1 && q == cylic2) {// 环上都只有一个节点
  46:         if (p != q) {
  47:             return null;// 不相交
  48:         }
  49:         return p;
  50:     } else if ((p == cylic1 && q != cylic2) || (p != cylic1 && q == cylic2)) {
  51:         // 某一个环上只有一个节点,另一个不止一个节点,一定不相交
  52:         return null;
  53:     }
  54:     // 两个环上都不止一个节点
  55:     if (cylic1 == cylic2) {// 共环且入口一样。但交点可能在环上cylic1节点,也可能不在环上
  56:         p = p1;
  57:         q = p2;
  58:         int i = 1, k = 1;
  59:         while (p != cylic1) {
  60:             p = p.getpNext();
  61:             i++;
  62:         }
  63:         while (q != cylic2) {
  64:             q = q.getpNext();
  65:             k++;
  66:         }
  67:         if (i == k) {
  68:             return cylic1;
  69:         }
  70:         p = p1;
  71:         q = p2;
  72:         if (i >= k) {// p1链比p2链长
  73:             for (int j = 0; j < i - k; j++) {
  74:                 p = p.getpNext();
  75:             }
  76:         } else {// p2链比p1链长
  77:             for (int j = 0; j < i - k; j++) {
  78:                 q = q.getpNext();
  79:             }
  80:         }
  81:         while (p != q) {
  82:             p = p.getpNext();
  83:             q = q.getpNext();
  84:         }
  85:         return p;
  86:     }
  87:  
  88:     p = cylic1.getpNext();
  89:     q = cylic2.getpNext();
  90:     while (p != cylic1) {
  91:         if (p == cylic2) {// p1与p2共环,但是入口不一样
  92:             return p;
  93:         }
  94:         p = p.getpNext();
  95:     }
  96:     return null;
  97: }
  98:  
  99: // 从起点开始,一个每次走一步,一个每次走两步,有环则两个节点在某一时刻会相交
 100: public Node hasCylic(Node head) {
 101:     Node p1 = head, p2 = head;
 102:     while (p2 != null && p2.getpNext() != null) {
 103:         p1 = p1.getpNext();
 104:         p2 = p2.getpNext().getpNext();
 105:         if (p1 == p2) {
 106:             return p1;
 107:         }
 108:     }
 109:     return null;
 110:  }


7. 复杂链表的复制

有一个复杂链表,其结点除了有一个 pNext 指针指向下一个结点外,还有一个pSibling指向链表中的任一结点或者 NULL。

                 

                         image

如图所示,有A、B、C、D四个节点,大致思路是先克隆,再分离。

   1: public ComplexNode clone(ComplexNode head) {
   2:     if (head == null) {
   3:         return null;
   4:     }
   5:     ComplexNode pComplexNode = head;
   6:     ComplexNode cloneNode;
   7:     do {// 复制节点
   8:         cloneNode = new ComplexNode();
   9:         cloneNode.setData(pComplexNode.getData() + 10);// 复制节点值
  10:         cloneNode.setpNext(pComplexNode.getpNext());// 复制节点与原节点指向相同的下一个节点
  11:         pComplexNode.setpNext(cloneNode);// 原节点的next指向复制节点
  12:         pComplexNode = cloneNode.getpNext();// 准备复制下一原节点
  13:     } while (pComplexNode != null);
  14:     pComplexNode = head;
  15:     do {// 复制pSinling
  16:         cloneNode = pComplexNode.getpNext();
  17:         if (pComplexNode.getpSinling() != null) {// 原节点的pSinling指向不为空
  18:             cloneNode.setpSinling(pComplexNode.getpSinling().getpNext());
  19:             // 复制节点的pSinling指向为原节点的pSinling指向的节点下一节点
  20:         }
  21:         pComplexNode = cloneNode.getpNext();
  22:     } while (pComplexNode != null);
  23:     pComplexNode = head;
  24:     ComplexNode newHead = pComplexNode.getpNext();
  25:     cloneNode = newHead;
  26:     do {// 断开链表
  27:         pComplexNode.setpNext(cloneNode.getpNext());// 原节点的pNext指向复制节点的pNext,即原始的下一节点
  28:         pComplexNode = pComplexNode.getpNext();// 指向原始的下一节点
  29:         if (pComplexNode != null) {
  30:             // 最末端时,pComplexNode ==null,pComplexNode.getpNext()会抛空指针异常
  31:             cloneNode.setpNext(pComplexNode.getpNext());// 复制节点的pNext,即复制节点的下一节点
  32:         } else {
  33:             cloneNode.setpNext(null);
  34:         }
  35:         cloneNode = cloneNode.getpNext();// 指向复制节点的下一节点
  36:     } while (cloneNode != null);
  37:     return newHead;
  38: }
0 0