date: 2016-08-18 9:12:22
title: 数据结构之链表面试题汇总(一)–查找单向链表的中间节点、倒数第K个节点
categories: 数据结构
版权声明:本站采用开放的[知识共享署名-非商业性使用-相同方式共享 许可协议]进行许可
所有文章出现的代码,将会出现在我的github中,名字可以根据类全名来找,我在github中的文件夹也会加目录备注。
这篇文章主要讨论单向链表的应用,大体结构请看文章目录结构。并且在涉及到的方法中,能够不借助容器来存储数据,就尽量不用。 每个系列的参考资料,都在序号最大的文章里面,如果是本篇文章特有的,请看文章结尾处的参考资料。
单向链表的创建和遍历(最基本,属于常识知识)
/**
* 定义单向链表
*
* @author XinPan
*
*/
public class Node {
// 记录链表的值
int record;
// 记录当前节点的下一个节点的引用
Node next;
// ------getter&setter-------------
public int getRecord() {
return record;
}
public void setRecord(int record) {
this.record = record;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
public Node(int record) {
super();
this.record = record;
}
}
/**
* 遍历单向链表
*
* @author XinPan
*
*/
public class RecustionAllElementOfLinkedList {
// 输出所有节点内容
public static void printAllElements(Node head) {
while (head != null) {
System.out.println(head.getRecord());
head = head.getNext();
}
}
// 获取指定链表的长度
public static int sizes(Node head) {
int length = 0;
if (head == null)
return length;
while (head != null) {
length++;
head = head.getNext();
}
return length;
}
}
查找单链表中的倒数第k个结点(剑指offer,题15)
思路:
1、考虑到把链表中的节点存到数组中,然后通过数组得到length-k位元素, 但是这种方法需要把全部节点放到数组中,浪费内存
2、既然把借用容器来存放节点这种方法否定掉,那么通过怎样的方式可以让它得到倒数第K个节点
3、考虑到不改变链表本身的结构,不借助容器,那么可以借用“指针”来完成这个问题
4、可以遍历两次链表,获取到链表的总长度之后,再遍历一次,遍历到第length-k个节点,然后返回, 可是这种方法消耗太大
5、可以借助两个“指针”直接在链表中,先让一个脚标移动到第k位节点,然后再跟第一个“指针”一起移动,当后面 的“指针”达到链表最后一个节点的时候,返回前面“指针”所对应的节点
图解:例子-获取倒数第二个节点
public class FindReciprocalElementOFLinkedList {
/**
* 查找单向链表的倒数第k位节点
* @param head 链表的头节点
* @param k 倒数第几位
* @return
*/
public static Node find(Node head, int k) {
// 若头节点为空或者k=0,抛异常
if (head == null || k == 0)
throw new RuntimeException(
"check your parms that was sent to this method");
// 声明两个变量来记录两个节点的引用
Node first = head;
Node second = head;
// 让second“指针”向后移动 k-1 位
for (int i = 1; i < k; i++) {
second = second.getNext();
// 注意:k的值有可能比链表的长度还大,所以在这里抛出异常
if (second == null)
throw new RuntimeException(
"the k is more than the size of this list");
}
// 一起移动两个节点引用直到第二个节点引用到达链表的最后一个节点
while (second.getNext() != null) {
first = first.getNext();
second = second.getNext();
}
// 返回第一个节点引用,即为倒数第k个节点
return first;
}
}
测试代码:
public class ReverseLinkedListTest {
public static void main(String[] args) {
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
Node node4 = new Node(4);
Node node5 = new Node(5);
Node node6 = new Node(6);
node1.setNext(node2);
node2.setNext(node3);
node3.setNext(node4);
node4.setNext(node5);
node5.setNext(node6);
System.out.println(FindReciprocalElementOFLinkedList.find(node1, 2)
.getRecord());
}
}
运行结果:
查找单向链表中的中间节点
思路:
1、我们可以继承上一题中使用双指针的思想来实现,这样既可以节省内存,又可以提高效率
2、让一个指针每次移动一位,另一个指针每次移动两位,当移动跨度大的指针到达结尾的时候,前面那个指针所对应的节点就是中间节点!
图解:
代码实现:
public class FindTheMidElement {
public static Node findMidNode(Node head) {
// 判断head是否为空,为空抛异常
if (head == null)
throw new RuntimeException("头节点为空");
// 声明两个变量来存储两个“指针”,并且让它们指向头节点
Node first = head;
Node second = head;
// 当后面的指针所指向的节点不为空并且其下一个节点不为空的时候,
while (second != null && second.getNext() != null) {
// 让前面指针每次移动一位,后面指针每次移动两位
first = first.getNext();
second = second.getNext().getNext();
}
// 说明后面的指针已经到结尾了,这时返回前面的指针
return first;
}
}
测试代码:节点数目为基数时:
public class ReverseLinkedListTest {
/**
* @param args
*/
public static void main(String[] args) {
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
Node node4 = new Node(4);
Node node5 = new Node(5);
node1.setNext(node2);
node2.setNext(node3);
node3.setNext(node4);
node4.setNext(node5);
System.out.println(FindTheMidElement.findMidNode(node1).getRecord());
}
}
运行结果:
节点数为偶数时:
public class ReverseLinkedListTest {
/**
* @param args
*/
public static void main(String[] args) {
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
Node node4 = new Node(4);
Node node5 = new Node(5);
Node node6 = new Node(6);
node1.setNext(node2);
node2.setNext(node3);
node3.setNext(node4);
node4.setNext(node5);
node5.setNext(node6);
System.out.println(FindTheMidElement.findMidNode(node1).getRecord());
}
}
运行结果: