链表基础训练(二)

题目一:

  编写代码,以给定值x为基准将链表分为两部分,所有小于x的结点排在大于或等于x的结点之前。给定一个链表的头结点 ListNode * pHead,请返回重新后的链表的头指针。注意:分割以后原来的数据顺序不变,不要开辟新的空间,即不要新建节点。比如 5 6 3 2 7 以 3 为基准分区后为2 5 6 3 7。

  思路:题目说不要新建节点,但是可以新建节点指针。定义左右两个链表指针,小于x的元素链接到左边链表,大于等于x的元素链接到右边链表,最后左右两个链表相连即可。这里需要注意边界的问题,假如左边的链表为空,那么直接返回右边链表的头指针。

 1 public class 链表分区 {
 2     private static class ListNode {
 3         int val;
 4         ListNode next = null;
 5 
 6         ListNode(int val) {
 7             this.val = val;
 8         }
 9 
10         @Override
11         public String toString() {
12             StringBuilder sb = new StringBuilder(val + "");
13             ListNode nnext = next;
14             while (nnext != null) {
15                 sb.append(nnext.val);
16                 nnext = nnext.next;
17             }
18             return sb.toString();
19         }
20     }
21     public static ListNode partition(ListNode pHead, int x) {
22         ListNode p = pHead;
23         ListNode leftFirst = null;
24         ListNode leftTail = null;
25         ListNode rightFirst = null;
26         ListNode rightTail = null;
27 
28         while (p != null) {//顺序扫描所有节点
29           int pValue = p.val;
30           if (pValue < x) {//小于x
31             if (leftTail == null) {
32               leftFirst = p;
33               leftTail = p;
34             } else {
35               leftTail.next = p;
36               leftTail = leftTail.next;
37             }
38           } else {//大于等于x
39             if (rightTail == null) {
40               rightFirst = p;
41               rightTail = p;
42             } else {
43               rightTail.next = p;
44               rightTail = rightTail.next;
45             }
46           }
47           p = p.next;
48         }
49         if (leftFirst == null) {// 左边链表可能为空
50           return rightFirst;
51         }
52         leftTail.next = rightFirst;//左右两个链表连接起来
53         if (rightTail != null)
54           rightTail.next = null;
55         return leftFirst;
56       }
57     public static void main(String[] args) {
58         int[] arr = { 5,6,3,2,7 };
59         ListNode head = new ListNode(arr[0]);// 哑元
60         ListNode p = head;
61         for (int i = 1; i < arr.length; i++) {
62             p.next = new ListNode(arr[i]);
63             p = p.next;
64         }
65         ListNode p1 = head.next;
66         ListNode res = partition(head, 3);
67         while (res != null) {
68             // 理解这里为什么通过res就能遍历链表  函数里是左右链表连接起来的
69             System.out.println(res.val);
70             res = res.next;
71         }
72     }
73 }
View Code

  结果:

    

题目二:

  链表加法:有两个用链表表示的整数,每个结点包含一个数位。这些数位是反向存放的,也就是个位排在链表的首部。编写函数对这两个整数求和,并用链表形式返回结果。给定两个链表ListNode* A,ListNode* B,请返回A+B的结果(ListNode*)。

  测试样例:{1,2,3},{3,2,1}

  返回:{4,4,4}

  {7,4,0,7,5},{2,7,2,3,4}   返回:{9,1,3,0,0,1}

  思路:一是使用for循环遍历两个链表把对应的位上的数字加起来,遍历链表的时候创建节点最后把相加后的链表的头结点返回。二是使用递归的方法,在递归的方法中创建新的节点,把当前两个对应的链表的数字加起来,然后剩下来的节点进行递归求解,把两张链表对应位相加后的结果的节点链接起来,最后得到的就是相加后的结果的链表,返回头指针就可以了。

 1 public class 链表加法 {
 2     public static ListNode plusAB(ListNode a, ListNode b) {
 3         return plusAB(a, b, 0);
 4     }
 5 
 6     private static ListNode plusAB(ListNode a, ListNode b, int i) {
 7         if (a == null && b == null && i == 0)
 8             return null;
 9         int value = i;
10         if (a != null)
11             value += a.val;
12         if (b != null)
13             value += b.val;
14         ListNode result = new ListNode(value % 10);
15         result.next = plusAB(a == null ? null : a.next, b == null ? null : b.next, value >= 10 ? 1 : 0);
16         return result;
17     }
18     
19     private static class ListNode {
20         int val;
21         ListNode next = null;
22 
23         ListNode(int val) {
24             this.val = val;
25         }
26 
27         @Override
28         public String toString() {
29             StringBuilder sb = new StringBuilder(val + "");
30             ListNode nnext = next;
31             while (nnext != null) {
32                 sb.append(nnext.val);
33                 nnext = nnext.next;
34             }
35             return sb.toString();
36         }
37     }
38 
39     public static void main(String[] args) {
40         
41         ListNode node1 = new ListNode(7);
42         node1.next = new ListNode(4);
43         node1.next.next = new ListNode(0);
44         node1.next.next.next = new ListNode(7);
45         node1.next.next.next.next = new ListNode(5);
46         System.out.println(node1);
47 
48         ListNode node2 = new ListNode(2);
49         node2.next = new ListNode(7);
50         node2.next.next = new ListNode(2);
51         node2.next.next.next = new ListNode(3);
52         node2.next.next.next.next = new ListNode(4);
53         System.out.println(node2);
54 
55         ListNode result = plusAB(node1, node2);
56         System.out.println(result);
57 
58     }
59 }
View Code

  结果:

    

题目三:

  给定一个有环链表,实现一个算法返回环路的开头结点。有环链表的定义:在链表中某个结点的next元素指向在它前面出现过的节点,则表明该链表存在环路。

  思路:解法一,使用HashSet判断是否有重复元素。解法二,使用了经典的技巧,快慢指针。首先可以使用快慢指针来判断链表是否有环。使用两个指针来进行移动,一个为快指针,一个为慢指针,快指针一次移动两步,慢指针一次移动一步,假如链表有环的话那么这两个指针一定会在某一个地方会相遇的,为什么呢?有没有可能不会相遇呢?答案是不可能的,两个指针一定会在某一个位置上相遇,因为假如它们没有相遇,快指针的位置为i + 1,那么慢指针的位置为i,那么退回到上一步的状态,那么两个指针的位置都是i-1,那么它是一定会相遇的。所以在有环的情况之下第二圈的时候快指针追慢指针那么最后一定会在某一个位置上相遇。假如链表是没有环的,那么慢指针一定会在走的时候而快指针就到达链表的末尾了,所以永远不可能两者相遇。

  假如题目要求不能使用额外的空间来进行记录,所以也可以使用快慢指针方式求出环起点。

    

  假设慢指针s走k步到了有环的节点,因为快指针f每次走两步,所以与有环节点的距离为k,那么这个时候应该是f追s,假设环的长度为l,所以s,f另外之间的距离为l - k,所以 f 要走多l - k的距离就可以追上s,与s相遇,由于每走一步s与f的距离就会减少1,所以当s走l - k步的时候那么两者就相遇了,这个时候相遇的点是关键,明显可以看出相遇的点与有环的点相距k的距离。而头结点到有环节点的距离也是k,那么只要s与头结点同时以同样的速度移动那么当他们相遇的时候该节点就是有环的节点了。所以我们可以在循环中判断两个指针是否相等,相等的时候跳出循环然后s与头结点同时以同样的速度移动那么当他们相遇的时候该节点就是环的起点了。

 1 import java.util.HashSet;
 2 
 3 public class 链表环起点 {
 4     private static class ListNode {
 5         int val;
 6         ListNode next = null;
 7 
 8         ListNode(int val) {
 9             this.val = val;
10         }
11 
12 //        @Override
13 //        public String toString() {
14 //            StringBuilder sb = new StringBuilder(val + "");
15 //            ListNode nnext = next;
16 //            while (nnext != null) {
17 //                sb.append(nnext.val);
18 //                nnext = nnext.next;
19 //            }
20 //            return sb.toString();
21 //        }
22     }
23     
24     // 解法一
25     public static ListNode check(ListNode head) {
26         ListNode p = head;
27         HashSet set = new HashSet();
28         while (true) {
29             if (set.contains(p))
30                 return p;
31             else {
32                 set.add(p);
33                 p = p.next;
34             }
35         }
36     }
37 
38     // 判断是否有环  快慢指针
39     public static boolean hasCircle(ListNode head) {
40         ListNode s = head;
41         ListNode f = head;
42         while (true) {
43             s = s.next;
44             f = f.next.next;
45             if (s == f)
46                 return true;
47             if (f.next == null)
48                 return false;
49         }
50     }
51 
52     // 解法二  求出环起点 这里面包含了判断是否满足有环
53     public static ListNode beginOfCircle(ListNode head) {
54         ListNode s = head;
55         ListNode f = head;
56         while (f != null && f.next != null) {
57             s = s.next;
58             f = f.next.next;
59             if (s == f)
60                 break;
61         }
62         // System.out.println(f); 
63         // 这里不能这样调用  因为默认是System.out.println(f.toString()) 
64         // 会陷入死循环 因为上面重写了toString()
65         // 何种方式退出的? 如果没环 返回null
66         // 下面 f == null f也会默认调用toString()方法,而导致堆溢出,
67         // 所以注释掉了toString()方法 运行成功
68         if (f == null || f.next == null) {
69             return null;
70         }
71         
72         ListNode p = head;
73         while (p != s) {
74             p = p.next;
75             s = s.next;
76         }
77         return p;
78     }
79 
80     public static void main(String[] args) {
81         ListNode node = new ListNode(1);
82         node.next = new ListNode(2);
83         ListNode begin = new ListNode(32);
84         node.next.next = begin;
85         node.next.next.next = new ListNode(32);
86         node.next.next.next.next = new ListNode(2);
87         node.next.next.next.next.next = begin;
88          ListNode res = check(node);
89          System.out.println("解法一:"+res.val);
90 
91         System.out.println(hasCircle(node));
92         System.out.println("解法二:"+beginOfCircle(node).val);
93     }
94 }
View Code

  结果:

    

 

猜你喜欢

转载自www.cnblogs.com/xiaoyh/p/10386535.html