單鏈表的建立和遍歷、求單鏈表中節點的個數、查找單鏈表中的中間結點、判斷單鏈表是否有環、取出有環鏈表中環的長度,刪除有序鏈表中的重複結點

一、單鏈表的建立和遍歷:html

1java

2node

3面試

4app

5ide

6測試

7ui

8this

9spa

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

public class LinkList {

public Node head;

public Node current;

 

//方法:向鏈表中添加數據

public void add(int data) {

 //判斷鏈表爲空的時候

 if (head == null) {//若是頭結點爲空,說明這個鏈表尚未建立,那就把新的結點賦給頭結點

 head = new Node(data);

 current = head;

 } else {

 //建立新的結點,放在當前節點的後面(把新的結點合鏈表進行關聯)

 current.next = new Node(data);

 //把鏈表的當前索引向後移動一位

 current = current.next; //此步操做完成以後,current結點指向新添加的那個結點

 }

}

 

//方法:遍歷鏈表(打印輸出鏈表。方法的參數表示從節點node開始進行遍歷

public void print(Node node) {

 if (node == null) {

 return;

 }

 

current = node;

 while (current != null) {

 System.out.println(current.data);

 current = current.next;

 }

}

 

class Node {

//注:此處的兩個成員變量權限不能爲private,由於private的權限是僅對本類訪問。

 int data; //數據域

 Node next;//指針域

 

 public Node(int data) {

 this.data = data;

}

}

 

 

public static void main(String[] args) {

LinkList list = new LinkList();

//向LinkList中添加數據

 for (int i = 0; i < 10; i++) {

 list.add(i);

 }

 

 list.print(list.head);// 從head節點開始遍歷輸出

}

 

}

 

上方代碼中,這裏面的Node節點採用的是內部類來表示(33行)。使用內部類的最大好處是能夠和外部類進行私有操做的互相訪問。

注:內部類訪問的特色是:內部類能夠直接訪問外部類的成員,包括私有;外部類要訪問內部類的成員,必須先建立對象。

爲了方便添加和遍歷的操做,在LinkList類中添加一個成員變量current,用來表示當前節點的索引(03行)。

這裏面的遍歷鏈表的方法(20行)中,參數node表示從node節點開始遍歷,不必定要從head節點遍歷。

 

二、求單鏈表中節點的個數:

注意檢查鏈表是否爲空。時間複雜度爲O(n)。這個比較簡單。

核心代碼:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

//方法:獲取單鏈表的長度

public int getLength(Node head) {

 if (head == null) {

 return 0;

 }

 

 int length = 0;

 Node current = head;

 while (current != null) {

 length++;

 current = current.next;

 }

 

 return length;

}

 

四、查找單鏈表中的中間結點:

一樣,面試官不容許你算出鏈表的長度,該怎麼作呢?

思路:

    和上面的第2節同樣,也是設置兩個指針first和second,只不過這裏是,兩個指針同時向前走,second指針每次走兩步,first指針每次走 一步,直到second指針走到最後一個結點時,此時first指針所指的結點就是中間結點。注意鏈表爲空,鏈表結點個數爲1和2的狀況。時間複雜度爲 O(n)。

代碼實現:

  

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

//方法:查找鏈表的中間結點

public Node findMidNode(Node head) {

 

if (head == null) {

 return null;

}

 

Node first = head;

 Node second = head;

//每次移動時,讓second結點移動兩位,first結點移動一位

while (second != null && second.next != null) {

 first = first.next;

 second = second.next.next;

}

 

//直到second結點移動到null時,此時first指針指向的位置就是中間結點的位置

 return first;

}

上方代碼中,當n爲偶數時,獲得的中間結點是第n/2 + 1個結點。好比鏈表有6個節點時,獲得的是第4個節點。

 

判斷單鏈表是否有環:

  這裏也是用到兩個指針,若是一個鏈表有環,那麼用一個指針去遍歷,是永遠走不到頭的。

  所以,咱們用兩個指針去遍歷:first指針每次走一步,second指針每次走兩步,若是first指針和second指針相遇,說明有環。時間複雜度爲O (n)。

方法:

  

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

//方法:判斷單鏈表是否有環

public boolean hasCycle(Node head) {

 

 if (head == null) {

 return false;

 }

 

 Node first = head;

 Node second = head;

 

 while (second != null) {

 first = first.next; //first指針走一步

 second = second.next.next; second指針走兩步

 

 if (first == second) { //一旦兩個指針相遇,說明鏈表是有環的

  return true;

 }

}

 

 return false;

}

完整版代碼:(包含測試部分)

這裏,咱們還須要加一個重載的add(Node node)方法,在建立單向循環鏈表時要用到。

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

LinkList.java:

 

 public class LinkList {

 public Node head;

 public Node current;

  

 //方法:向鏈表中添加數據

 public void add(int data) {

  //判斷鏈表爲空的時候

  if (head == null) {//若是頭結點爲空,說明這個鏈表尚未建立,那就把新的結點賦給頭結點

  head = new Node(data);

  current = head;

 } else {

  //建立新的結點,放在當前節點的後面(把新的結點合鏈表進行關聯)

  current.next = new Node(data);

  //把鏈表的當前索引向後移動一位

  current = current.next;

  }

 }

  

  

 //方法重載:向鏈表中添加結點

 public void add(Node node) {

  if (node == null) {

  return;

  }

  

  if (head == null) {

  head = node;

  current = head;

  } else {

  current.next = node;

  current = current.next;

  }

 }

  

  

 //方法:遍歷鏈表(打印輸出鏈表。方法的參數表示從節點node開始進行遍歷

 public void print(Node node) {

  if (node == null) {

  return;

  }

  

  current = node;

  while (current != null) {

  System.out.println(current.data);

  current = current.next;

  }

 }

  

 //方法:檢測單鏈表是否有環

 public boolean hasCycle(Node head) {

  

  if (head == null) {

  return false;

  }

  

  Node first = head;

  Node second = head;

  

  while (second != null) {

  first = first.next; //first指針走一步

  second = second.next.next; //second指針走兩步

  

  if (first == second) { //一旦兩個指針相遇,說明鏈表是有環的

   return true;

  }

  }

  

  return false;

 }

  

 class Node {

  //注:此處的兩個成員變量權限不能爲private,由於private的權限是僅對本類訪問。

  int data; //數據域

  Node next;//指針域

  

  public Node(int data) {

  this.data = data;

  }

 }

  

 public static void main(String[] args) {

  LinkList list = new LinkList();

  //向LinkList中添加數據

  for (int i = 0; i < 4; i++) {

  list.add(i);

  }

  

  list.add(list.head); //將頭結點添加到鏈表當中,因而,單鏈表就有環了。備註:此時獲得的這個環的結構,是下面的第8小節中圖1的那種結構。

  

  System.out.println(list.hasCycle(list.head));

 }

 }

檢測單鏈表是否有環的代碼是第50行。

88行:咱們將頭結點繼續往鏈表中添加,此時單鏈表就環了。最終運行效果爲true。

若是刪掉了88行代碼,此時單鏈表沒有環,運行效果爲false。

 

取出有環鏈表中,環的長度:

咱們平時碰到的有環鏈表是下面的這種:(圖1)

上圖中環的長度是4。

但有可能也是下面的這種:(圖2)

此時,上圖中環的長度就是3了。

那怎麼求出環的長度呢?

 

//方法:判斷單鏈表是否有環。返回的結點是相遇的那個結點

public Node hasCycle(Node head) {

 

 if (head == null) {

 return null;

 }

 

 Node first = head;

 Node second = head;

 

while (second != null) {

 first = first.next;

 second = second.next.next;

 

 if (first == second) { //一旦兩個指針相遇,說明鏈表是有環的

  return first; //將相遇的那個結點進行返回

 }

 }

 return null;

}

 

//方法:有環鏈表中,獲取環的長度。參數node表明的是相遇的那個結點

public int getCycleLength(Node node) {

 

 if (head == null) {

 return 0;

 }

 

 Node current = node;

 int length = 0;

 

 while (current != null) {

 current = current.next;

 length++;

 if (current == node) { //當current結點走到原點的時候

  return length;

 }

 }

 return length;

}

 

完整版代碼:(包含測試部分)

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

public class LinkList {

 public Node head;

 public Node current;

  

 public int size;

  

 //方法:向鏈表中添加數據

 public void add(int data) {

  //判斷鏈表爲空的時候

  if (head == null) {//若是頭結點爲空,說明這個鏈表尚未建立,那就把新的結點賦給頭結點

  head = new Node(data);

  current = head;

  } else {

  //建立新的結點,放在當前節點的後面(把新的結點合鏈表進行關聯)

  current.next = new Node(data);

  //把鏈表的當前索引向後移動一位

  current = current.next; //此步操做完成以後,current結點指向新添加的那個結點

  }

 }

  

  

 //方法重載:向鏈表中添加結點

 public void add(Node node) {

  if (node == null) {

  return;

  }

  if (head == null) {

  head = node;

  current = head;

  } else {

  current.next = node;

  current = current.next;

  }

 }

  

  

 //方法:遍歷鏈表(打印輸出鏈表。方法的參數表示從節點node開始進行遍歷

 public void print(Node node) {

  if (node == null) {

  return;

  }

  

  current = node;

  while (current != null) {

  System.out.println(current.data);

  current = current.next;

  }

 }

  

 //方法:判斷單鏈表是否有環。返回的結點是相遇的那個結點

 public Node hasCycle(Node head) {

  

  if (head == null) {

  return null;

  }

  

  Node first = head;

  Node second = head;

  

  while (second != null) {

  first = first.next;

  second = second.next.next;

  

  if (first == second) { //一旦兩個指針相遇,說明鏈表是有環的

   return first; //將相遇的那個結點進行返回

  }

  }

  

  return null;

 }

  

 //方法:有環鏈表中,獲取環的長度。參數node表明的是相遇的那個結點

 public int getCycleLength(Node node) {

  

  if (head == null) {

  return 0;

  }

  

  Node current = node;

  int length = 0;

  

  while (current != null) {

  current = current.next;

  length++;

  if (current == node) { //當current結點走到原點的時候

   return length;

  }

  }

  

  return length;

 }

  

 class Node {

  //注:此處的兩個成員變量權限不能爲private,由於private的權限是僅對本類訪問。

  int data; //數據域

  Node next;//指針域

  

  public Node(int data) {

  this.data = data;

  }

 }

  

  

 public static void main(String[] args) {

  LinkList list1 = new LinkList();

  

  Node second = null; //把第二個結點記下來

  

  //向LinkList中添加數據

  for (int i = 0; i < 4; i++) {

  list1.add(i);

  if (i == 1) {

   second = list1.current; //把第二個結點記下來

  }

  }

  

  list1.add(second); //將尾結點指向鏈表的第二個結點,因而單鏈表就有環了,備註:此時獲得的環的結構,是本節中圖2的那種結構

  Node current = list1.hasCycle(list1.head); //獲取相遇的那個結點

  

  System.out.println("環的長度爲" + list1.getCycleLength(current));

 }

  

 }

 運行效果:

若是將上面的104至122行的測試代碼改爲下面這樣的:(即:將圖2中的結構改爲圖1中的結構)

?

1

2

3

4

5

6

7

8

9

10

11

12

13

public static void main(String[] args) {

  LinkList list1 = new LinkList();

  //向LinkList中添加數據

  for (int i = 0; i < 4; i++) {

   list1.add(i);

  }

 

  list1.add(list1.head); //將頭結點添加到鏈表當中(將尾結點指向頭結點),因而,單鏈表就有環了。備註:此時獲得的這個環的結構,是本節中圖1的那種結構。

 

  Node current = list1.hasCycle(list1.head);

 

  System.out.println("環的長度爲" + list1.getCycleLength(current));

}

 

運行結果:

若是把上面的代碼中的第8行刪掉,那麼這個鏈表就沒有環了,因而運行的結果爲0。

 

 

刪除有序鏈表中的重複結點

一,問題描述

請本身構造一個簡單的有序單鏈表,而後實現刪除鏈表中的重複結點。好比:

 

二,問題分析

首先要實現一個單鏈表,所以須要定義一個節點類Node。其次,實現向鏈表中添加結點的方法(使用尾插法)addNode

刪除重複結點的實現思路:

定義兩個指針:pre 和 next。初始時,pre指向鏈表中的第一個元素,next指向鏈表中的第二個元素。若是 pre 的值與 next 的值不相等,則兩個指針分別都向後移一個結點;若相等,則刪除 next 指針指向的結點便可。

 

三,整個代碼實現

複製代碼

// delete duplicated nodes in increased list
public class MyLinkedList {

    private class Node{
        int ele;
        Node next;
        public Node(int ele) {
            this.ele = ele;
            next = null;
        }
    }
    
    private Node head;
    private Node tail;
    
    //採用尾插法添加結點
    public void addNode(int ele){
        Node newNode = new Node(ele);
        if(tail != null)
            tail.next = newNode;
        else{// first node
            head = newNode;
        }
        tail = newNode;
    }
    
    //刪除有序單鏈表中的重複結點
    public void delDuplicatedNode(){
        if(head == null)
            return;
        Node pre,next;
        pre = head;
        next = head.next;
        
        while(next != null)
        {
            if(pre.ele != next.ele)
            {
                pre = next;
                next = next.next;
            }else{//delete next point node
                Node delNode = next;
                pre.next = next.next;
                next = next.next;
                delNode.next = null;//avoid memory leak
//                delNode = null;
            }
        }
    }
    
    @Override
    public String toString() {
        if(head == null)
            return "null";
        Node current = head;
        StringBuilder sb = new StringBuilder();
        while(current != null){
            sb.append(current.ele + " ");
            current = current.next;
        }
        return sb.toString();
    }
    
    //hapjin test
    public static void main(String[] args) {
        MyLinkedList mylist = new MyLinkedList();
        int[] eles = {1,2,3,3,4,4,5};
        for (int ele : eles) {
            mylist.addNode(ele);
        }
        System.out.println("before del: " + mylist);
        mylist.delDuplicatedNode();
        System.out.println("after del: " + mylist);
    }
}
相關文章
相關標籤/搜索