单链表的常见面试题有如下:
1.求单链表中有效节点的个数
**思路:**查找有效节点的个数,首先想到的就是遍历这个链表
代码演示:
//head 为头结点
public static int getLength(StudentNode head){
if (head.next == null){
//说明这是一个空链表
return 0;
}
int length =0;
//定义了一个辅助接点,让辅助节点指向了头下一个节点
StudentNode node = head.next;
while(node !=null){
length++;
node = node.next;
}
return length;
}
}
主函数代码直接调用方法:
//表示头结点不存储任何数据
private StudentNode head = new StudentNode(0,"","");
public StudentNode getHead() {
return head;
}
System.out.println("有效的节点个数为:"+getLength(linkedList.getHead()));
代码运行如下:
2.查找单链表中的倒数第K个节点(新浪面试题)
思路:
1.编写一个方法,接受head节点,同时接受一个index
2.index:表示是倒数的第index个节点
3.先把链表从头到尾遍历,得到了链表的总的长度,然后在用总的长度减去index就实现了倒数的查找.(length-index = k)
4.如果找到了这个k,就返回该节点,否则就返回空
代码实现:
public static StudentNode findLastNode(StudentNode head,int index){
if (head.next ==null){
return null;
}
//第一次遍历得到链表的长度
int size = getLength(head);
//第二次遍历
//遍历的次数为:size-index
//先做一个index的校验
if (index <= 0||index > size){
return null;
}
//定义一个辅助变量,for循环定位到倒数的index个
StudentNode temp = head.next;
for (int i = 0; i <size-index ; i++) {
//size是总长度,index是倒数的节点,这里的for循环是为了找到我们要移动这个辅助变量多少次,
temp = temp.next;
}
return temp;
}
主函数代码:
//测试是否得到第k个节点
StudentNode res = findLastNode(linkedList.getHead(),1);
System.out.println("res="+res);
代码运行如下图:
3.单链表的反转(腾讯面试题)
思路:
1.先定义一个节点,reverseHead = new StudentNode();
2.从头到尾遍历原来的链表,每遍历一个节点,就将其取出,并放到新的链表reverseHead的最前端,
3.遍历到的第二个节点会将其放在前一个节点的前端,循环如此
4.最后再将原先的head节点指向这个新节点的头部
代码实现:
public static void reverseList(StudentNode head){
//当前列表为空,或者说只有一个节点
if (head.next == null||head.next.next == null){
return;
}
//定义一个辅助的变量,帮助我们遍历原来的链表
StudentNode current = head.next;
StudentNode next = null;//指向当前节点temp的下一个节点
StudentNode reverseHead = new StudentNode(0,"","");//新的节点的头结点
//遍历原来的链表,每遍历一个节点就将其取出,并放在新的链表reverseHead的最前端
while(current != null){
next = current.next;//先暂时保存当前节点的下一个节点,后面需要
current.next = reverseHead.next;//将current的下一个节点指向新的链表的头结点的下一个节点
reverseHead.next = current;
current= next;//让current后移,由于将current节点拿走了,那么我们需要保留current的下一个节点,防止链表断裂
}
//将head.next指向reverseHead.next,实现单链表的反转
head.next = reverseHead.next;
}
主函数代码:
System.out.println("测试一个原链表的情况===========");
linkedList.list();
System.out.println("测试一个反转单链表的情况======");
reverseList(linkedList.getHead());
linkedList.list();
这里面有一个需要思考的代码,就是这一段:
//定义一个辅助的变量,帮助我们遍历原来的链表
StudentNode current = head.next;
StudentNode next = null;//指向当前节点temp的下一个节点
StudentNode reverseHead = new StudentNode(0,"","");//新的节点的头结点
//遍历原来的链表,每遍历一个节点就将其取出,并放在新的链表reverseHead的最前端
while(current != null){
next = current.next;//先暂时保存当前节点的下一个节点,后面需要
current.next = reverseHead.next;//将current的下一个节点指向新的链表的头结点的下一个节点
reverseHead.next = current;
current= next;//让current后移,由于将current节点拿走了,那么我们需要保留current的下一个节点,防止链表断裂
}
//将head.next指向reverseHead.next,实现单链表的反转
head.next = reverseHead.next;
}
这里我讲讲我的理解:
1.首先,我们定义了一个原先表的辅助变量为current
StudentNode current = head.next;
2. 接着我们定义了一个原先链表的next节点,这个节点将它当成这个current的下一个节点,这里只做了定义没做赋值,
StudentNode next = null;
//指向当前节点current的下一个节点
3.我们定义了一个新的链表头,目的就是将原先链表中后面节点的值取出来,然后在放到新的链表中去
StudentNode reverseHead = new StudentNode(0,"","");
//新的节点的头结点
4.然后我们执行循环,遍历出原先链表中的每一个节点
while(current != null)
//这里简单的做了一个判断原先链表不为空
5.最重要的部分来了,我们将当前节点,也就是头结点的下一个节点(第一个有值的节点)赋值给next,(具体next看2)
next = current.next;
//先暂时保存当前节点的下一个节点,后面需要
6.当current的下一个节点指向reverseHead的下一个节点,也就是将原先遍历到的每一个节点都赋给新的头结点的next
current.next = reverseHead.next;
//将current的下一个节点指向新的链表的头结点的下一个节点
7.我们将current,赋给了新的链表的头结点的下一个节点
reverseHead.next = current;
8.因为我们前面已经存好了当前节点的下一个节点,所以说:这一步我们再将第一个有值节点的下一个节点赋给current(现在的当前节点),这一步就是为了将currnet后移
current= next;
//让current后移,由于将current节点拿走了,那么我们需要保留current的下一个节点,防止链表断裂
9.最后一步:我们将新的链表反转了,但是头结点并不是原先的哪一个,所以我们这一步就是将反转后的新头结点,赋值给原来的头结点.这样最终实现了原先节点的反转
//将head.next指向reverseHead.next,实现单链表的反转
head.next = reverseHead.next;
4.从尾到头打印单链表(百度:要求方式:反向遍历,方式二:Stack栈)
思路分析:
1.从尾到头打印链表,就是逆序打印单链表
方式一:先将单链表反转过来,再进行遍历打印(问题:只是打印,但是破坏了单链表的结构,不建议)
方式二:可以利用栈这个数据结构,将各个节点压入栈中,再利用栈的先进后出的特点,实现了逆序打印的效果(栈:)
代码演示:
//使用方式二,进行逆序的打印这个链表.可以利用栈的这个结构,将各个节点压入到栈中然后再利用栈的先进后出的特点
public static void reversePrint(StudentNode head){
if (head.next ==null){
System.out.println("该链表为空,无法打印这个链表");
return;
}
Stack<StudentNode> stack = new Stack<>();
StudentNode current = head.next;
//将链表的所有节点压入栈中
while (current != null){
stack.push(current);
//每压入一次就往后面移动一次
current = current.next;
}
//将栈中的节点打印出来
while(stack.size()>0){
System.out.println(stack.pop());
}
}
主函数代码:
System.out.println("测试逆序打印单链表");
reversePrint(linkedList.getHead());
代码运行结果展示:
5.合并两个有序的单链表,合并之后的链表依然有序
思路分析:合并两个有序的单链表,可以再利用一个新的链表来进行操作
1.我们不知道两个有序的单链表那个key值更小,所以我们需要进行判断,从小到大排列,
2.比较两个链表中的最小值,并放到新的链表中依次执行
代码展示:
创建两个链表代码:
//先创建几个节点
StudentNode student1 = new StudentNode(1,"qiuzhikang","91");
StudentNode student3 =new StudentNode(3,"chenxiaocong","81");
StudentNode student5 =new StudentNode(5,"diyifei","71");
StudentNode student7 =new StudentNode(7,"xujinbo","61");
//第二个链表
StudentNode student2 = new StudentNode(2,"tangzaizai","11");
StudentNode student4 =new StudentNode(4,"wulinjie","21");
StudentNode student6 =new StudentNode(6,"dengshiyuan","31");
StudentNode student8 =new StudentNode(8,"honghuanming","41");
//创建一个链表
SingleLinkedList linkedList = new SingleLinkedList();
SingleLinkedList linkedList2 = new SingleLinkedList();//为了合并两个有序的链表,我再新建一个有序的链表
// linkedList.add(student1);
// linkedList.add(student2);
// linkedList.add(student3);
// linkedList.add(student4);
//加入按照编号的顺序
linkedList.addByRank(student1);
linkedList.addByRank(student3);
linkedList.addByRank(student5);
linkedList.addByRank(student7);
//第二个有序链表
linkedList2.addByRank(student2);
linkedList2.addByRank(student4);
linkedList2.addByRank(student6);
linkedList2.addByRank(student8);
//5.合并两个有序的单链表,合并之后的链表依然有序
public static void formTwoLinkedList(StudentNode head,StudentNode head2) throws Exception {
StudentNode temp1 = head.next;
StudentNode temp2 = head2.next;
//新的链表节点
StudentNode newNode = new StudentNode(0,"","");
//接下来做判断
//1.第一个链表没有节点,则只显示第二个链表的节点
if (head.next ==null){
newNode.next= head2.next;
}
if (head2.next ==null){
newNode.next= head.next;
}
if (head == null&&head2 == null){
throw new Exception("两张链表均为空");
}
StudentNode temp3 = newNode;//新节点的临时变量
//第一个链表不为空,或者第二个链表不为空
//1.链表一不为空,链表二为空.2链表一位空,链表二不为空.3.链表一不为空,链表二不为空
while(temp1!=null || temp2 != null) {
if (temp1 != null && temp2 == null) {
temp3.next = temp1;//让temp3指向temp1的节点
temp1 = temp1.next;
} else if (temp1 == null && temp2 != null) {
temp3.next = temp2;//让temp3指向temp2的节点
temp2 = temp2.next;
} else {
//都不为空的情况下,合并两个有序的链表
if (temp1.rank >= temp2.rank) {
temp3.next = temp2;
temp2 = temp2.next;
} else {
temp3.next = temp1;
temp1 = temp1.next;
}
}
temp3 = temp3.next;
}
SingleLinkedList list = new SingleLinkedList();
list.show(newNode);
}
主函数代码:
formTwoLinkedList(linkedList.getHead(),linkedList2.getHead());
代码执行结果如下图: