JAVA实现链表面试题.docx

上传人:b****5 文档编号:6439501 上传时间:2023-01-06 格式:DOCX 页数:49 大小:157.76KB
下载 相关 举报
JAVA实现链表面试题.docx_第1页
第1页 / 共49页
JAVA实现链表面试题.docx_第2页
第2页 / 共49页
JAVA实现链表面试题.docx_第3页
第3页 / 共49页
JAVA实现链表面试题.docx_第4页
第4页 / 共49页
JAVA实现链表面试题.docx_第5页
第5页 / 共49页
点击查看更多>>
下载资源
资源描述

JAVA实现链表面试题.docx

《JAVA实现链表面试题.docx》由会员分享,可在线阅读,更多相关《JAVA实现链表面试题.docx(49页珍藏版)》请在冰豆网上搜索。

JAVA实现链表面试题.docx

JAVA实现链表面试题

JAVA实现链表面试题

这篇文章主要介绍了JAVA相关实现链表的面试题,代码实现非常详细,每一个方法讲解也很到位,特别适合参加Java面试的朋友阅读。

这份笔记整理了整整一个星期,每一行代码都是自己默写完成,并测试运行成功,同时也回

顾了一下《剑指offer》这本书中和链表有关的讲解,希望对笔试和面试有所帮助。

本文包含链表的以下内容:

1、单链表的创建和遍历

2、求单链表中节点的个数

3、查找单链表中的倒数第k个结点(剑指offer,题15)

4、查找单链表中的中间结点

5、合并两个有序的单链表,合并之后的链表依然有序【出现频率高】(剑指offer,题

17)

6、单链表的反转【出现频率最高】(剑指offer,题16)

7、从尾到头打印单链表(剑指offer,题5)

8、判断单链表是否有环

9、取出有环链表中,环的长度

10、单链表中,取出环的起始点(剑指offer,题56)。

本题需利用上面的第8题和第9题。

11、判断两个单链表相交的第一个交点(剑指offer,题37)

1、单链表的创建和遍历:

1

publicclassLinkList{

2

publicNodehead;

3

publicNodecurrent;

4

//方法:

向链表中添加数据

5

publicvoidadd(intdata){

6

//判断链表为空的时候

7

if(head==null){//如果头结点为空,说明这个链表还没有创建,那就把新的结点赋给头

8

结点

9

head=newNode(data);

10

current=head;

11

}else{

12

//创建新的结点,放在当前节点的后面(把新的结点合链表进仃关联)

13

current.next=newNode(data);

14

//把链表的当前索引向后移动一位

15

current=current.next;//此步操作完成之后,current结点指向新添加的那个结点

16

}

17

}

18

//方法:

遍历链表(打印输出链表。

方法的参数表示从节点node开始进仃遍历

19

publicvoidprint(Nodenode){

20

if(node==null){

21

return;

22

}

23

current=node;

24

while(current!

=nuII){

25

System.out.println(current.data);

26

current=current.next;

27

}

28

}

29

classNode{

30

//注:

此处的两个成员变量权限不能为

private,因为private的权限是仅对本类访冋。

31

intdata;//数据域

32

Nodenext;//指针域

33

publicNode(intdata){

34

this.data=data;

35

}

36

}

37

publicstaticvoidmain(String[]args){

38

LinkListlist=newLinkList();

39

//向LinkList中添加数据

40

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

41

list.add(i);

42

}

43

list.print(list.head);//从head节点开始遍历输出

44

}

45

}

46

47

48

49

50

51

52

53

上方代码中,这里面的Node节点采用的是内部类来表示(33行)。

使用内部类的最大好处

是可以和外部类进行私有操作的互相访问。

注:

内部类访问的特点是:

内部类可以直接访问外部类的成员,包括私有;外部类要访问内

部类的成员,必须先创建对象。

为了方便添加和遍历的操作,在LinkList类中添加一个成员变量current,用来表示当前节点

的索引(03行)。

这里面的遍历链表的方法(20行)中,参数node表示从node节点开始遍历,不一定要从head节点遍历。

2、求单链表中节点的个数:

注意检查链表是否为空。

时间复杂度为0(n)。

这个比较简单。

核心代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

//方法:

获取单链表的长度

publicintgetLength(Nodehead){if(head==null){

return0;

}

intlength=0;

Nodecurrent=head;

while(current!

=nuII){

length++;

current=current.next;

}

returnlength;

}

3、查找单链表中的倒数第k个结点:

3.1普通思路:

先将整个链表从头到尾遍历一次,计算出链表的长度size,得到链表的长度之后,就好办了,直接输出第(size-k)个节点就可以了(注意链表为空,k为0,k为1,k大于链表中节点个数

时的情况

)。

时间复杂度为0(n),大概思路如下:

publicintfindLastNode(intindex){//index代表的是倒数第index的那个结点

//第一次遍历,得到链表的长度size

if(head==null){

return-1;

}

current=head;

while(current!

=nuII){

size++;

current=current.next;

}

//第二次遍历,输出倒数第index个结点的数据

current=head;

for(inti=0;i

current=current.next;

}

returncurrent.data;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

如果面试官不允许你遍历链表的长度,该怎么做呢?

接下来就是。

3.2改进思路:

(这种思路在其他题目中也有应用)

这里需要声明两个指针:

即两个结点型的变量first和second,首先让first和second都指向第一个结点,然后让second结点往后挪k-1个位置,此时first和second就间隔了k-1个位置,然后整体向后移动这两个节点,直到second节点走到最后一个结点的时候,此时first

节点所指向的位置就是倒数第k个节点的位置。

时间复杂度为0(n)

代码实现:

(初版)

publicNodefindLastNode(Nodehead,intindex){

if(node==null){

returnnuII;

}

Nodefirst=head;

Nodesecond=head;

//让second结点往后挪index个位置

for(inti=0;i

second=second.next;

}

//让first和second结点整体向后移动,直到second结点为Null

while(second!

=nuII){

first=first.next;

second=second.next;

}

//当second结点为空的时候,此时first指向的结点就是我们要找的结点

returnfirst;

}

代码实现:

(最终版)(考虑k大于链表中结点个数时的情况时,抛出异常)上面的代码中,看似已经实现了功能,其实还不够健壮:

要注意k等于0的情况;

如果k大于链表中节点个数时,就会报空指针异常,所以这里需要做一下判断。

核心代码如下:

1publicNodefindLastNode(Nodehead,intk){

2if(k==0||head==null){

3returnnuII;

4}

5Nodefirst=head;

6

Nodesecond=head;

7

//让second结点往后挪k-1个位置

8

for(inti=0;i

9

System.out.println("i的值是"+i);

10

second=second.next;

11

if(second==null){//说明k的值已经大于链表的长度了

12

//thrownewNullPointerException(”链表的长度小于”+k);//我们自己抛出异常,给用户

13

以提示

14

returnnuII;

15

}

16

}

17

//让first和second结点整体向后移动,直到second走到最后一个结点

18

while(second.next!

=null){

19

first=first.next;

20

second=second.next;

21

}

22

//当second结点走到最后一个节点的时候,此时first指向的结点就是我们要找的结点

23

returnfirst;

24

}

25

26

4、查找单链表中的中间结点:

同样,面试官不允许你算出链表的长度,该怎么做呢?

思路:

和上面的第2节一样,也是设置两个指针first和second,只不过这里是,两个指针同时向

前走,second指针每次走两步,first指针每次走一步,直到second指针走到最后一个结点

时,此时first指针所指的结点就是中间结点。

注意链表为空,链表结点个数为1和2的情

况。

时间复杂度为0(n)。

代码实现:

1

//方法:

查找链表的中间结点

2

publicNodefindMidNode(Nodehead){

3

if(head==null){

4

returnnuII;

5

}

6

Nodefirst=head;

7

Nodesecond=head;

8

//每次移动时,让second结点移动两位,first结点移动一位

9

while(second!

=null&&second.next!

=nuII){

10

first=first.next;

11

second=second.next.next;

12

}

13

//直到second结点移动到null时,此时first指针指向的位置就是中间结点的位置

14returnfirst;

15}

16

17

18

上方代码中,当n为偶数时,得到的中间结点是第n/2+1个结点。

比如链表有6个节点时,

得到的是第4个节点。

5、合并两个有序的单链表,合并之后的链表依然有序:

这道题经常被各公司考察。

例如:

链表1:

1->2->3->4

链表2:

2->3->4->5

合并后:

1->2->2->3->3->4->4->5

解题思路:

挨着比较链表1和链表2。

这个类似于归并排序。

尤其要注意两个链表都为空、和其中一个为空的情况。

只需要0

(1)的空间。

时间复杂度为O(max(len1,len2))

代码实现:

1

:

//两个参数代表的是两个链表的头结点

2

publicNodemergeLinkList(Nodehead1,Nodehead2){

3

if(head1--null&&head2--null){//如果两个链表都为空

4

returnnuII;

5

}

6

if(head1==null){

7

returnhead2;

8

}

9

if(head2==null){

10

returnhead1;

11

}

12

Nodehead;//新链表的头结点

13

Nodecurrent;//current结点指向新链表

14

//一开始,我们让current结点指向head1和head2中较小的数据,得到head结点

15

if(head1.data

16

head=head1;

17

current=head1;

18

head1=head1.next;

19

}else{

20

head=head2;

21

current=head2;

44

45

46

47

48

49

代码测试:

publicstaticvoidmain(String[]args){

LinkListlist1=newLinkList();

LinkListlist2=newLinkList();

//向LinkList中添加数据

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

list1.add(i);

}

for(inti=3;i<8;i++){

9

10

list2.add(i);

}

LinkListlist3=newLinkList();

12

13

list3.head=list3.mergeLinkList(list1.head,list2.head);//将list1和list2合并,存放到list3中

14Iist3.print(list3.head);〃从head节点开始遍历输出

15}

16

17

上方代码中用到的add方法和print方法和第1小节中是一致的。

运行效果:

Run:

■LinkList■LinkList■LinkList■LinkList

卜書C:

\Java\jdkl,7

■*1

II那2

3

®H3

■a昏4

w6

7

vProcessfinishedwithexitcodee

r

注:

《剑指offer》中是用递归解决的,感觉有点难理解。

6、单链表的反转:

【出现频率最高】例如链表:

1->2->3->4

反转之后:

4->2->2->1

思路:

从头到尾遍历原链表,每遍历一个结点,将其摘下放在新链表的最前端。

注意链表为空

和只有一个结点的情况。

时间复杂度为0(n)

方法1:

(遍历)

14

current=next;//操作结束后,current节点后移

15

}

16

returnreverseHead;

17

}

18

19

20

21

22

23

上方代码中,核心代码是第16、17行。

方法2:

(递归)

这个方法有点难,先不讲了。

7、从尾到头打印单链表:

对于这种颠倒顺序的问题,我们应该就会想到栈,后进先出。

所以,这一题要么自己使

用栈,要么让系统使用栈,也就是递归。

注意链表为空的情况。

时间复杂度为0(n)

注:

不要想着先将单链表反转,然后遍历输出,这样会破坏链表的结构,不建议。

方法1:

(自己新建一个栈)

1

2

3

//方法:

从尾到头打印单链表

4

publicvoidreversePrint(Nodehead){

5

if(head==null){

6

return;

7

}

8

Stackstack=newStack();//新建一个栈

9

Nodecurrent=head;

10

//将链表的所有结点压栈

11

while(current!

=nuII){-

12

stack.push(current);//将当前结点压栈

13

current=current.next;

14

}

15

//将栈中的结点打印输出即可

16

while(stack.size()>0){

17

System.out.println(stack.pop().data);//出栈操作

18

}

19

}

20

21

方法2:

(使用系统的栈:

递归,代码优雅简洁)

1publicvoidreversePrint(Nodehead){

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

2

if(head==nuII){

3

return;

4

}

5

reversePrint(head.next);

6

System.out.println(head.data);

7

}

8

9

总结:

方法2是基于递归实现的,戴安看起来简洁优雅,但有个问题:

当链表很长的时候,

就会导致方法调用的层级很深,有可能造成栈溢出。

而方法1的显式用栈,是基于循环实现

的,代码的鲁棒性要更好一些。

&判断单链表是否有环:

这里也是用到两个指针,如果一个链表有环,那么用一个指针去遍历,是永远走不到头

的。

因此,我们用两个指针去遍历:

first指针每次走一步,second指针每次走两步,如果first指针和second指针相遇,说明有环。

时间复杂度为0(n)。

方法:

//方法:

判断单链表是否有环

publicbooleanhasCycle(Nodehead){

if(head==null){

returnfalse;

}

Nodefirst=head;

Nodesecond=head;

while(second!

=nuII){

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

second=second.next.next;second指针走两步

if(first==second){//一旦两个指针相遇,说明链表是有环的returntrue;

}

}

returnfalse;

}

完整版代码:

(包含测试部分)

这里,我们还需要加一个重载的add(Nodenode)方法,在创建单向循环链表时要用到。

1LinkList.java:

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

publicclassLinkList{

publicNodehead;

publicNodecurrent;

//方法:

向链表中添加数据

publicvoidadd(intdata){

//判断链表为空的时候

if(head==null){//如果头结点为空,说明这个链表还没有创建,那就把新的结点赋给头

结点

head=newNode(data);

current=head;

}else{

//创建新的结点,放在当前节点的后面(把新的结点合链表进行关联)

current.next=newNode(data);

//把链表的当前索引向后移动一位

current=current.next;

}

}

//方法重载:

向链表中添加结点

publicvoidadd(Nodenode){

if(node==null){

return;

}

if(head==null){

head=node;

current=head;

}else{

current.next=node;

current=current.next;

}

}

//方法:

遍历链表(打印输出链表。

方法的参数表示从节点node开始进行遍历

publicvoidprint(Nodenode){

if(node==null){

return;

}

current=node;

while(current!

=nuII){

System.out.println(current.data);

current=current.next;

}

}

//方法:

检测单链表是否有环

publicbooleanhasCycle(Nodehead){

if(head==null){

}

Nodefirst=head;

Nodesecond=head;

while(second!

=nuII){

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

second=second.next.next;//second指针走两步

if(first==second){//一旦两个指针相遇,说明链表是有环的returntrue;

}

}

returnfalse;

}

classNode{

//注:

此处的两个成员变量权限不能为private,因为private的权限是仅对本类访问。

intdata;//数据域

Nodenext;//指针域

publicNode(intdata){

this.data=data;

}

}

publicstaticvoidmain(String[]args){

LinkListlist=newLinkList(www.hunanwang.net);

//向LinkList中添加数据

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

list.add(i);

}

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 考试认证 > 从业资格考试

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1