java链表link_LinkList(双向链表实现)

linkedlist是用链表结构存储数据的,比较适合数据的动态插入和删除,随机访问和遍历速度比较慢,还提供了list接口i中没有定义的方法,专门用于操作表头和表尾的元素,所以可以当作堆栈、队列和双向队列来使用。linkedlist持有头节点和尾节点的引用,有两个构造器,一个是无参构造器,另一个是传入外部集合构造器,它没有像arraylist一样的初始大小的构造器。

1 //集合元素个数

2

3 transient int size = 0;

4

5

6

7 //头结点引用

8

9 transient node first;

10

11

12

13 //尾节点引用

14 transient node last;

15

16 //无参构造器

17 public linkedlist() {}

18

19 //传入外部集合的构造器

20 public linkedlist(collection extends e> c) {

21 this();

22 addall(c);

23 }

24

25 //增(添加)

26 public boolean add(e e) {

27

28 //在链表尾部添加

29 linklast(e);

30 return true;

31 }

32

33

34 //增(插入)

35 public void add(int index, e element) {

36

37 checkpositionindex(index);

38

39 if (index == size) {

40 //在链表尾部添加

41 linklast(element);

42 } else {

43 //在链表中部插入

44 linkbefore(element, node(index));

45 }

46 }

47

48

49

50 //删(给定下标)

51 public e remove(int index) {

52

53 //检查下标是否合法

54 checkelementindex(index);

55 return unlink(node(index));

56 }

57

58 //删(给定元素)

59 public boolean remove(object o) {

60 if (o == null) {

61 for (node x = first; x != null; x = x.next) {

62 if (x.item == null) {

63 unlink(x);

64 return true;

65 }

66 }

67 } else {

68 //遍历链表

69 for (node x = first; x != null; x = x.next) {

70 if (o.equals(x.item)) {

71 //找到了就删除

72 unlink(x);

73 return true;

74 }

75 }

76 }

77 return false;

78 }

79

80

81

82 //改

83 public e set(int index, e element) {

84

85 //检查下标是否合法

86 checkelementindex(index);

87

88 //获取指定下标的结点引用

89 node x = node(index);

90

91 //获取指定下标结点的值

92

93 e oldval = x.item;

94

95 //将结点元素设置为新的值

96

97 x.item = element;

98

99 //返回之前的值

100

101 return oldval;

102

103 }

104

105

106 //查

107 public e get(int index) {

108

109 //检查下标是否合法

110 checkelementindex(index);

111

112 //返回指定下标的结点的值

113 return node(index).item;

114 }

linkedlist的添加元素的方法主要是调用linklast和linkbefore两个方法,linklast方法是在链表后面链接一个元素,linkbefore方法是在链表中间插入一个元素。linkedlist的删除方法通过调用unlink方法将某个元素从链表中移除。下面是链表的插入和删除操作的核心代码。

1 //链接到指定结点之前

2 void linkbefore(e e, node succ) {

3

4 //获取给定结点的上一个结点引用

5 final node pred = succ.prev;

6

7 //创建新结点, 新结点的上一个结点引用指向给定结点的上一个结点

8 //新结点的下一个结点的引用指向给定的结点

9 final node newnode = new node<>(pred, e, succ);

10

11 //将给定结点的上一个结点引用指向新结点

12 succ.prev = newnode;

13

14 //如果给定结点的上一个结点为空, 表明给定结点为头结点

15 if (pred == null) {

16

17 //将头结点引用指向新结点

18 first = newnode;

19 } else {

20 //否则, 将给定结点的上一个结点的下一个结点引用指向新结点

21 pred.next = newnode;

22 }

23

24 //集合元素个数加一

25 size++;

26

27 //修改次数加一

28 modcount++;

29

30 }

31

32

33

34 //卸载指定结点

35 e unlink(node x) {

36

37 //获取给定结点的元素

38 final e element = x.item;

39

40 //获取给定结点的下一个结点的引用

41 final node next = x.next;

42

43 //获取给定结点的上一个结点的引用

44 final node prev = x.prev;

45

46 //如果给定结点的上一个结点为空, 说明给定结点为头结点

47 if (prev == null) {

48

49 //将头结点引用指向给定结点的下一个结点

50 first = next;

51 } else {

52 //将上一个结点的后继结点引用指向给定结点的后继结点

53 prev.next = next;

54 //将给定结点的上一个结点置空

55 x.prev = null;

56

57 }

58

59 //如果给定结点的下一个结点为空, 说明给定结点为尾结点

60 if (next == null) {

61

62 //将尾结点引用指向给定结点的上一个结点

63 last = prev;

64 } else {

65

66 //将下一个结点的前继结点引用指向给定结点的前继结点

67 next.prev = prev;

68 x.next = null;

69

70 }

71

72 //将给定结点的元素置空

73 x.item = null;

74

75 //集合元素个数减一

76 size--;

77 //修改次数加一

78 modcount++;

79 return element;

80

81 }

通过源码的分析,对链表的插入和删除的时间复杂度都是o(1),而对链表的查找和修改操作都需要遍历链表进行元素的定位,这两个操作都是调用的node(int index) 方法定位元素,如下代码演示根据下标来定位元素。

1 //根据指定位置获取结点

2 node node(int index) {

3 //如果下标在链表前半部分, 就从头开始查起

4 if (index < (size >> 1)) {

5 node x = first;

6 for (int i = 0; i < index; i++) {

7 x = x.next;

8 }

9 return x;

10 } else {

11 //如果下标在链表后半部分, 就从尾开始查起

12 node x = last;

13 for (int i = size - 1; i > index; i--) {

14 x = x.prev;

15 }

16 return x;

17 }

18 }

通过下标定位时先判断是在链表的上半部还是下半部

上半部:从头开始找;

下半部:从尾开始找;

因此通过下标的查找和修改操作的时间复杂度是o(n/2),通过对双向链表的操作还可以实现单项队列,双向队列和栈的功能。

单向队列的操作的代码:

1 //获取头结点

2 public e peek() {

3 final node f = first;

4 return (f == null) ? null : f.item;

5 }

6

7 //获取头结点

8 public e element() {

9 return getfirst();

10 }

11

12 //弹出头结点

13 public e poll() {

14 final node f = first;

15 return (f == null) ? null : unlinkfirst(f);

16 }

17

18 //移除头结点

19 public e remove() {

20 return removefirst();

21 }

22

23 //在队列尾部添加结点

24 public boolean offer(e e) {

25 return add(e);

26 }

双向队列的操作:

1 //在头部添加

2 public boolean offerfirst(e e) {

3 addfirst(e);

4 return true;

5 }

6

7 //在尾部添加

8 public boolean offerlast(e e) {

9 addlast(e);

10 return true;

11 }

12

13 //获取头结点

14 public e peekfirst() {

15 final node f = first;

16 return (f == null) ? null : f.item;

17 }

18

19 //获取尾结点

20 public e peeklast() {

21 final node l = last;

22 return (l == null) ? null : l.item;

23 }

栈操作:

1 //入栈

2 public void push(e e) {

3 addfirst(e);

4 }

5

6 //出栈

7 public e pop() {

8 return removefirst();

9 }

对lindedlist,有:

1. linkedlist是基于双向链表实现的,不论是增删改查方法还是队列和栈的实现,都可通过操作结点实现

2. linkedlist无需提前指定容量,因为基于链表操作,集合的容量随着元素的加入自动增加

3. linkedlist删除元素后集合占用的内存自动缩小,无需像arraylist一样调用trimtosize()方法

4. linkedlist的所有方法没有进行同步,因此它也不是线程安全的,应该避免在多线程环境下使用

5. 以上分析基于jdk1.7,其他版本会有些出入,因此不能一概而论

以上内容部分借鉴于:https://blog.csdn.net/iteen/article/details/83109717

希望与广大网友互动??

点此进行留言吧!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值