单链表的建立和遍历、求单链表中节点的个数、查找单链表中的中间结点、判断单链表是否有环、取出有环链表中环的长度,删除有序链表中的重复结点

一、单链表的建立和遍历: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);
    }
}
相关文章
相关标签/搜索