Java 链表

链表是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。

使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表双向链表以及循环链表。链表可以在多种编程语言中实现。像Lisp和Scheme这样的语言的内建数据类型中就包含了链表的存取和操作。程序语言或面向对象语言,如C,C++和Java依靠易变工具来生成链表。

 

链表特点

单链表,箭头末尾为结点

线性表的链式存储表示的特点是用一组任意的存储单元存储线性表数据元素(这组存储单元可以是连续的,也可以是不连续的)。因此,为了表示每个数据元素 与其直接后继数据元素 之间的逻辑关系,对数据元素 来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。由这两部分信息组成一个"结点"(如概述旁的图所示),表示线性表中一个数据元素。线性表的链式存储表示,有一个缺点就是要找一个数,必须要从头开始找起,十分麻烦。

根据情况,也可以自己设计链表的其它扩展。但是一般不会在边上附加数据,因为链表的点和边基本上是一一对应的(除了第一个或者最后一个节点,但是也不会产生特殊情况)。不过有一个特例是如果链表支持在链表的一段中把前和后指针反向,反向标记加在边上可能会更方便。

对于非线性的链表,可以参见相关的其他数据结构,例如树、图。另外有一种基于多个线性链表的数据结构:跳表,插入、删除和查找等基本操作的速度可以达到O(nlogn),和平衡二叉树一样。

其中存储数据元素信息的域称作数据域(设域名为data),存储直接后继存储位置的域称为指针域(设域名为next)。指针域中存储的信息又称做指针或链。

由分别表示,,…,的N 个结点依次相链构成的链表,称为线性表的链式存储表示,由于此类链表的每个结点中只包含一个指针域,故又称单链表或线性链表。

链表基本操作

pascal语言

链表建立

第一行读入n,表示n个数

第二行包括n个数

以链表的形式存储输出这些数

?


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

programproject1;

type

point=^node;

node=record

data:longint;

next:point;

end;

var

i,n,e:longint;

p,q,head,last:point;

begin

write(‘Inputthenumbercount:‘);

readln(n);

i:=1;

new(head);

read(e);

head^.data:=e;

head^.next:=nil;

last:=head;

q:=head;

whilei<ndo

begin

inc(i);

read(e);

new(p);

q^.next:=p;

p^.data:=e;

p^.next:=nil;

last:=p;

q:=last

end;

//建立链表

q:=head;

whileq^.next<>nildo

begin

write(q^.data,‘‘);

q:=q^.next;

end;

write(q^.data);

//输出

readln;

readln

end.

删除

在以z为头的链表中搜索第一个n,如果找到则删去,返回值为1,否则返回0

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

functiondelete(n:longint;varz:point):longint;

var

t,s:point;

begin

t:=z;

while(t^.next<>nil)and(t^.data<>n)do

begin

s:=t;

t:=t^.next;

end;

ift^.data<>nthenexit(0);

s^.next:=t^.next;

dispose(t);

exit⑴

end;

链表查找

类似于删除,只需要找到不删即可

插入

插入,在以zz为头的链表第w个的前面插入nn元素,函数返回值正常是0,如果w超过了链表的长度,函数返回链表的长度

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

functioninsert(w,nn:longint;varzz:point):longint;

vard:longint;v,vp,vs:point;

begin

v:=zz;

ford:=1towdo

ifv^.next=nil

thenexit(d)

else

begin

vp:=v;

v:=v^.next;

end;

new(vs);

vs^.data:=nn;

vp^.next:=vs;

vs^.next:=v;

exit(0)

end;

链表链表函数

?


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

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

#include<stdio.h>

#include<stdlib.h>

#include<iostream.h>

usingnamespacestd;

structNode

{

intdata;//数据域

structNode*next;//指针域

};

/*

Create

*函数功能:创建链表.

*输入:各节点的data

*返回值:指针head

*/

Node*Create()

{

intn=0;

Node*head,*p1,*p2;

p1=p2=newNode;

cin>>p1->data;

head=NULL;

while(p1->data!=0)

{

if(n==0)

{

head=p1;

}

else

p2->next=p1;

p2=p1;

p1=newNode;

cin>>p1->data;

n++;

}

p2->next=NULL;

returnhead;

}

/*

insert

*函数功能:在链表中插入元素.

*输入:head链表头指针,p新元素插入位置,x新元素中的数据域内容

*返回值:无

*/

voidinsert(Node*head,intp,intx)

{

Node*tmp=head;//for循环是为了防止插入位置超出了链表长度

for(inti=0;i<p;i++)

{

if(tmp==NULL)

return;

if(i<p-1)

tmp=tmp->next;

}

Node*tmp2=newNode;

tmp2->data=x;

tmp2->next=tmp->next;

tmp->next=tmp2;

}

/*

del

*函数功能:删除链表中的元素

*输入:head链表头指针,p被删除元素位置

*返回值:被删除元素中的数据域.如果删除失败返回-1

*/

intdel(Node*head,intp)

{

Node*tmp=head;

for(inti=0;i<p;i++)

{

if(tmp==NULL)

return-1;

if(i<p-1)

tmp=tmp->next;

}

intret=tmp->next->data;

tmp->next=tmp->next->next;

returnret;

}

voidprint(Node*head)

{

for(Node*tmp=head;tmp!=NULL;tmp=tmp->next)

printf("%d",tmp->data);

printf("\n");

}

intmain()

{

Node*head;

head=newNode;

head->data=-1;

head->next=NULL;

return0;

}

例子

#include<iostream>

#defineNULL0

structstudent

{

longnum;

structstudent*next;

};

intmain()

{

inti,n;

student*p=(structstudent*)malloc(sizeof(structstudent));

student*q=p;

printf("输入几个值");

scanf("%d",&n);

for(i=1;i<=n;i++)

{

scanf("%d",&(q->num));

q->next=(structstudent*)malloc(sizeof(structstudent));

q=q->next;

}

printf("值第几个");

intrank;

scanf("%d%d",&(q->num),&rank);

student*w=p;

for(i=1;i<rank-1;i++)

{

w=w->next;

}

q->next=w->next;

w->next=q;

for(i=1;i<=n+1;i++)

{

printf("%d",p->num);

p=p->next;

}

return0;

}//指针后移麻烦链表形式循环链表

循环链表是与单链表一样,是一种链式的存储结构,所不同的是,循环链表的最后一个结点的指针是指向该循环链表的第一个结点或者表头结点,从而构成一个环形的链。

循环链表的运算与单链表的运算基本一致。所不同的有以下几点:

1、在建立一个循环链表时,必须使其最后一个结点的指针指向表头结点,而不是象单链表那样置为NULL。此种情况还使用于在最后一个结点后插入一个新的结点。

2、在判断是否到表尾时,是判断该结点链域的值是否是表头结点,当链域值等于表头指针时,说明已到表尾。而非象单链表那样判断链域值是否为NULL。

双向链表

双向链表其实是单链表的改进。

当我们对单链表进行操作时,有时你要对某个结点的直接前驱进行操作时,又必须从表头开始查找。这是由单链表结点的结构所限制的。因为单链表每个结点只有一个存储直接后继结点地址的链域,那么能不能定义一个既有存储直接后继结点地址的链域,又有存储直接前驱结点地址的链域的这样一个双链域结点结构呢?这就是双向链表

在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点地址,一般称之为右链域;一个存储直接前驱结点地址,一般称之为左链域。

应用举例概述

约瑟夫环问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。例如:n = 9,k = 1,m = 5

参考代码

?


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

#include<stdio.h>

#include<malloc.h>

#defineN41

#defineM5

typedefstructnode*link;

structnode

{

intitem;

linknext;

};

linkNODE(intitem,linknext)

{

linkt=malloc(sizeof*t);

t->item=item;

t->next=next;

returnt;

}

intmain(void)

{

inti;

linkt=NODE(1,NULL);

t->next=t;

for(i=2;i<=N;i++)

t=t->next=NODE(i,t->next);

while(t!=t->next)

{

for(i=1;i<M;i++)

t=t->next;

t->next=t->next->next;

}

printf("%d\n",t->item);

return0;

}

其他相关结语与个人总结

C语言是学习数据结构的很好的学习工具。理解了C中用结构体描述数据结构,那么对于理解其C++描述,Java描述都就轻而易举了!

链表的提出主要在于顺序存储中的插入和删除的时间复杂度是线性时间的,而链表的操作则可以是常数时间的复杂度。对于链表的插入与删除操作,个人做了一点总结,适用于各种链表如下:

插入操作处理顺序:中间节点的逻辑,后节点逻辑,前节点逻辑。按照这个顺序处理可以完成任何链表的插入操作。

删除操作的处理顺序:前节点逻辑,后节点逻辑,中间节点逻辑。

按照此顺序可以处理任何链表的删除操作。

如果不存在其中的某个节点略过即可。

上面的总结,大家可以看到一个现象,就是插入的顺序和删除的顺序恰好是相反的,很有意思!

操作

-----悉尼大学工程学院张志刚(Stone Cold)作品

?


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

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

#include<stdio.h>

#include<stdlib.h>

#include<conio.h>

typedefstructSlist

{

intdata;

structSlist*next;

}

SLIST;

SLIST*InitList_Sq()/*初始化函数*/

{

inta;

SLIST*h,*s,*r;

h=(SLIST*)malloc(sizeof(SLIST));/*建立头指针,头指针不可以更改!!!*/

r=h;

if(!h)

{

printf("分配失败");

exit(0);

}

scanf("%d",&a);

for(;a!=-1;)

{

s=(SLIST*)malloc(sizeof(SLIST));/*每次都开辟一个结点空间并赋值*/

s->data=a;

r->next=s;

r=s;

scanf("%d",&a);

}

r->next=‘\0‘;

returnh;

}

voidprint_list(SLIST*finder)/*打印函数*/

{

while(finder!=‘\0‘)

{

printf("->%d",finder->data);

finder=finder->next;

}

printf("->end\n");

}

intDeleteNode(SLIST*killer)//删除节点函数

{

inti,j=0;

SLIST*p,*q;

intx;

p=killer;

q=killer->next;

printf("请输入您要删除的节点序号:");

scanf("%d",&i);

while((p->next!=‘\0‘)&&(j<i-1))

{

p=p->next;

j++;

q=p->next;

}

if(p->next==‘\0‘||j>i-1)

{

printf("\nerror");

return-1;

}

else

{

p->next=q->next;

x=q->data;

free(q);

returnx;

}

}

voidInsert_Node(SLIST*jumper)//插入函数,本算法为前插结点法

{

intt,e,j=0;

SLIST*p,*q;

p=jumper;

printf("请输入要插入位置的序号:");

scanf("%d",&t);

printf("请输入要插入的元素:");

scanf("%d",&e);

while(p->next!=‘\0‘&&j<t-1)

{

j++;

p=p->next;

}

if(p==‘\0‘||j>t-1)

printf("插入的目的位置不存在");

else

{

q=(SLIST*)malloc(sizeof(SLIST));

q->data=e;

q->next=p->next;

p->next=q;

}

}

voidLocate_List(SLIST*reader)//查找值为e的元素

{

inte,i=0;

SLIST*p;

p=reader;

printf("请输入要查找的元素:");

scanf("%d",&e);

while(p->next!=‘\0‘&&p->data!=e)

{

i++;

p=p->next;

}

if(p->data==e)

printf("此元素在%d号位置\n",i);

else

printf("无此元素!");

}

voidmain()

{

inti,k,y;

SLIST*head;

printf("\n1.建立线性表");

printf("\n2.在i位置插入元素e");

printf("\n3.删除第i个元素,返回其值");

printf("\n4.查找值为e的元素");

printf("\n5.结束程序运行");

printf("\n===================================================");

printf("请输入您的选择:");

scanf("%d",&k);

switch(k)

{

case1:

{

head=InitList_Sq();

print_list(head->next);

}break;

case2:

{

head=InitList_Sq();

print_list(head->next);

Insert_Node(head);

print_list(head->next);

}

break;

case3:

{

head=InitList_Sq();

print_list(head->next);

y=DeleteNode(head);

print_list(head->next);

if(y!=-1)

printf("被删除元素为:%d",y);

}break;//头结点不算,从有数据的开始算第一个

case4:

{

head=InitList_Sq();

print_list(head->next);

Locate_List(head);

}break;

}

}

本程序可在微软VC++下编译通过并且运行

使用方法简介:运行程序后,先打数字1,然后回车,这样就可以先创建一个新的链表,比如你要创建一个

4->5->6->7这样一个链表,你就输入数字4回车,输入5回车,输入6回车,输入7回车,最后输入-1回车,这个-1就是告诉程序到此为止的标志

假如你要使用插入的功能位置插入,就输入3,回车,程序会问你插入的数值是什么,比如你要插入999,然后回车,999就被插进去了

其他的功能都大同小异

时间: 2024-10-12 14:05:21

Java 链表的相关文章

Java链表的删除操作

刚开始接触java时很长一段时间, 总觉得java链表的删除操作自己写的有bug. 第一个bug版本: 仅接removeByForlist.remove(j)之后应该显示调用i-- public static void testRemoveByFor() { List<Integer> removeByForlist = Lists.newArrayList(1, 1, 1, 1, 1, 1, 1, 1, 1, 1); System.out.println(ToStringBuilder.re

【数据结构与算法】java链表操作

链表操作代码量少但是比较容易出错,是比较适合面试的地方. 代码实现 /** * 源码名称:MyLinkList.java * 日期:2014-09-05 * 程序功能:java链表操作 * 版权:[email protected] * 作者:A2BGeek */ import java.util.Stack; public class MyLinkList { class LinkNode<T> { private T mValue; private LinkNode<T> mNe

JAVA 链表操作:循环链表

主要分析示例: 一.单链表循环链表 二.双链表循环链表 其中单链表节点和双链表节点类和接口ICommOperate<T>与上篇一致,这里不在赘述.参考:JAVA链表操作:单链表和双链表http://www.cnblogs.com/xiaoxing/p/5969133.html 一.单链表循环链表 package LinkListTest; import java.util.HashMap; import java.util.Map; public class SingleCycleLinkLi

Java链表基本操作和Java.util.ArrayList

Java链表基本操作和Java.util.ArrayList 今天做了一道<剑指offer>上的一道编程题"从尾到头打印链表",具体要求如下:输入一个链表,按链表值从尾到头的顺序返回一个ArrayList. 一开始我想的是通过两次遍历链表,第一次遍历得到链表元素的个数count.然后定义一个ArrayList 变量,由于ArrayList是动态数组,不能在未初始化的情况下对任意位置进行插入指定的值.所以只能先对其进行初始化,将count个ArrayList元素赋值为初始值0

用内部类和外部类实现java链表

在这里我将使用外部类和内部类两种方法来实现Java的链表,参考了java老师上课讲过的代码~ 主要思想是:首先要有一个Node类(节点类),其成员变量为String 类型的name,还有一个Node类型的next,作为指向下一个节点的指针:然后会设计增删查改四个方法(addNode().printNode().searchNode()还有deleteNode() 这里作简写~).然后是一个LinkList类,它首先会有一个根节点(Node 类型的root),然后是增删查改四个方法,不要忘了还有创

java链表

使用Java实现链表,首先定义链表的数据结构,也就是定义一个类,LinkedListNode.这个定义了链表的节点,链表节点分两部分,数据info和链接link. public class LinkedListNode {   public int info;   public LinkedListNode link;//这里就是链接,指向下一个节点的内存地址} link开始没看懂,怎么link是LinkListNode类型的呢?link不是指向下一个节点的内存地址吗?对,这里LinkedLis

Java链表实现

import java.util.Scanner; class DATA2 {     String key;                 //结点的关键字     String name;     int age; } class CLType                                //定义链表结构 {     DATA2 nodeData=new DATA2();     CLType nextNode;             //追加结点     CLType

JAVA链表实现与链表的逆序

1.链表         链表有单链表还有双向链表,在java中每个节点都是一个类,这里声明了内部类,为了方便插入和删除,有时会在,链表的表头加上哨兵(形成有哨兵的循环或双向循环链表),这样的插入删除就不用考虑当插入删除的元素在边界的情况,方便,至于哨兵你可能觉得没什么用,其实他可以存储链表的许多信息,比如你每次查找一个元素,在无哨兵时,使用P!=null,使用哨兵,可以存储要查找的元素,退出时,就要判断是不是哨兵. 2.链表的实现与逆序 若想代码写得快,必须清楚每次交换插入,产生的状态和性质.

JAVA 链表操作:单连表和双链表

主要讲述几点: 一.链表的简介 二.链表实现原理和必要性 三.单链表示例 四.双链表示例 一.链表的简介 链表是一种比较常用的数据结构,链表虽然保存比较复杂,但是在查询时候比较便捷,在多种计算机语言都相应的应用,链表有多种类别,文章针对单链表和双链表进行分析.链表中数据就像被一个链条串联一起,轻易的可以实现数据的访问. 二.链表实现原理和必要性 这里只分析单链表和双链表.链表的实现过程是有些许复杂的,但是会带来许多好处.比如现在网购时代到来,商家发快递一般会将商品包装在盒子里并写上地址信息,快递