关于链表的基本实现在上另一篇博客里面有写http://blog.csdn.net/a447332241/article/details/78947827
这里简单总结一下一些常见的链表面试题
1.链表反转
2.为尾到头打印链表
3.合并有序链表
4.判断链表是否有环
5.求链表的倒数第k个结点
1.链表反转
我们定义三个指针,分别记录当前遍历到的结点,它的前一个结点以及后一个结点,然后依次遍历,将当前结点的指针指向上一个结点。
public static Node reverse(Node head)
{
if(head==null||head.next==null)
return head;
Node cur = head.next; //当前反转结点结点
Node pre = head; //前一个结点
Node tmp=null; //中间结点 保存当前结点的下一个结点位置
while(cur!=null)
{
tmp=cur.next; //保存下一个结点
cur.next=pre; //反转指向
//向后遍历
pre=cur;
cur=tmp;
}
head.next=null;
return pre;
}
2.从尾到头打印链表
对于这种颠倒顺序的问题,我们应该就会想到栈,后进先出。所以,这一题要么自己使用栈,要么让系统使用栈,也就是递归。注意链表为空的情况。时间复杂度为O(n)
注:不要想着先将单链表反转,然后遍历输出,这样会破坏链表的结构,不建议。
//方法:从尾到头打印单链表
public static void reversePrint(Node head) {
if (head == null) {
return;
}
Stack<Node> stack = new Stack<Node>(); //新建一个栈
Node current = head;
//将链表的所有结点压栈
while (current != null) {
stack.push(current); //将当前结点压栈
current = current.next;
}
//将栈中的结点打印输出即可
while (stack.size() > 0) {
System.out.println(stack.pop().data); //出栈操作
}
}
使用系统的栈,递归实现
//使用系统的栈
public static void reversePrint1(Node head) {
if (head == null) {
return;
}
reversePrint(head.next);
System.out.println(head.data);
}
3.合并有序链表
这里提供两种方法1.类似于归并排序。尤其要注意两个链表都为空、和其中一个为空的情况。只需要O (1) 的空间。时间复杂度为O (max(len1,len2))
// 两个参数代表的是两个链表的头结点
public Node mergeLinkList(Node head1, Node head2) {
if (head1 == null && head2 == null) { // 如果两个链表都为空
return null;
}
if (head1 == null) {
return head2;
}
if (head2 == null) {
return head1;
}
Node head; // 新链表的头结点
Node current; // current结点指向新链表
// 一开始,我们让current结点指向head1和head2中较小的数据,得到head结点
if (head1.data < head2.data) {
head = head1;
current = head1;
head1 = head1.next;
} else {
head = head2;
current = head2;
head2 = head2.next;
}
while (head1 != null && head2 != null) {
if (head1.data < head2.data) {
current.next = head1; // 新链表中,current指针的下一个结点对应较小的那个数据
current = current.next; // current指针下移
head1 = head1.next;
} else {
current.next = head2;
current = current.next;
head2 = head2.next;
}
}
// 合并剩余的元素
if (head1 != null) { // 说明链表2遍历完了,是空的
current.next = head1;
}
if (head2 != null) { // 说明链表1遍历完了,是空的
current.next = head2;
}
return head;
}
2.利用递归实现
// 两个参数代表的是两个链表的头结点
public static Node merage(Node head1, Node head2) {
if (head1 == null)
return head2;
if (head2 == null)
return head1;
Node head3 = null;
if (head1.data < head2.data) {
head3 = head1;
head3.next = merage(head1.next, head2);
} else {
head3 = head2;
head3.next = merage(head1, head2.next);
}
return head3;//返回合并后链表的头结点
}
4.判断链表是否有环
思路:定义两个指针,我们用两个指针去遍历:first指针每次走一步,second指针每次走两步,如果first指针和second指针相遇,说明有环。
// 判断单链表是否有环,我们用两个指针去遍历:
// first指针每次走一步,second指针每次走两步,如果first指针和second指针相遇,说明有环。
public static boolean hasCycle(Node head) {
if (head == null) {
return false;
}
Node first = head;
Node second = head;
while (second != null) {
first = first.next; // first指针走一步
second = second.next.next; // second指针走两步
if (first == second) { // 一旦两个指针相遇,说明链表是有环的
return true;
}
}
return false;
}
5.求链表的倒数第k个结点
常规思路:假设整个链表有n个结点,那么倒数第k个结点就是从头结点开始的第n-k+1个结点,所以我们可以遍历链表两次,第一次统计出链表中结点的个数,第二次我们就能找到倒数第k个结点了。但是面试官一般只要求遍历链表一次,这就需要另外一种解法了。
我们定义两个指针,第一个指针从链表的头指针开始遍历向前走k-1步,第二个指针保持不动:从第k步开始,第二个指针也开始从链表的头指针开始遍历,由于两个指针的距离保持在k-1,当第一个(走在前面的)指针到达链表的尾结点时,第二个指针(走在后面的)指针正好是倒数第k个结点.。
public static Node FindKthtoTail(Node head, int k) {
if (head == null || k == 0)
return null;
Node slow = head;
Node fast = head;
for (int i = 0; i < k - 1; i++)
fast = fast.next;
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
相关题目:
1求链表的中间结点.如果链表的总数是奇数,返回中间结点;如果是偶数,返回中间两个结点的任意一个。为了解决这个问题,我们也可以定义两个指针,同时从链表的头结点出发,一个指针一次走一步,另一个指针一次走两步,当走得快的指针走到链表的末尾时,走得慢的指针正好在链表的中间.
2.判断一个链表是否又环,在上面已经有说到.