直接上干货。。。。。
链表常见题型:
先给出链表的定义:
/**
* 单链表定义
*/
public static class Node<E>{
private E element;//节点保存的元素
private Node<E> next;//指向下一个节点的引用
public Node(){}
public Node(E element){
this.element = element;
}
public Node(E element,Node<E> next){
this.element = element;
this.next = next;
}
public E getElement(){
return element;
}
public Node<E> getNext(){
return next;
}
public void setElement(E element){
this.element = element;
}
public void setNext(Node<E> next){
this.next = next;
}
}
关于鲁棒性:
鲁棒性也成为健壮性,是指程序能够判断输入是否合乎规范要求,并对不合要求的输入予以合理的处理。容错性是鲁棒性的一个重要体现。提高代码鲁棒性的有效途径是进行防御性编程,防御性编程是一种编程习惯,是指预见在什么地方可能会出现问题,并为这些可能出现问题制定处理方式。在面试时,最常用也是最有效的防御性编程是在函数入口添加代码以验证用户输入是否符合要求。
这里假设最后一个节点为倒数第一个。思想:快慢指针。实现代码如下:
public static Node<Integer> solution(Node<Integer> head,int k){
// 判断k是否非法
if(k < 1){return null;}
// 判断是否为空
if(head == null) return null;
// 快指针
Node<Integer> nAhead = head;
// 慢指针
Node<Integer> nBehind = head;
// 快指针先走k-1步
for(int i = 0;i<k-1;i++){
// 判断是否存在倒数第k个节点
if(nAhead.getNext() != null)
nAhead = nAhead.getNext();
else
return null;
}
// 同时向后移动
while(nAhead.getNext() != null){
nAhead = nAhead.getNext();
nBehind = nBehind.getNext();
}
return nBehind;
}
假定你只能访问该节点。思想:互换位置。实现代码如下:
// 不知道头节点,并且无法删除尾节点。
public static boolean solution(Node<Integer> node){
// 节点为空判断
if(node == null) return false;
// 节点为最后一个节点
if(node.getNext() == null) return false;
Node<Integer> temp = node.getNext();
// 交换节点存储数据
node.setElement(temp.getElement());
node.setNext(temp.getNext());
return true;
}
注意:若知道头节点的话,能够保证平均时间复杂度为O(1)。若不知道头节点想删除尾节点的话,只能标记一下尾节点。
代码如下:
public static Node<Integer> solution(Node<Integer> head){
//头节点为空
if(head == null) return head;
//只有一个节点
if(head.getNext() == null) return head;
//记录最后被反转的节点
Node<Integer> temp1 = head;
//记录将要反转的节点
Node<Integer> temp2 = head;
//记录将要反转的节点的下一个节点
Node<Integer> temp3 = head.getNext();
while(temp3 != null){
temp2 = temp3;
temp3 = temp2.getNext();
temp2.setNext(temp1);
temp1 = temp2;
}
head.setNext(null);
head = temp1;
/*while(temp1 != null){
System.out.print(temp1.getElement() + ",");
temp1 = temp1.getNext();
}*/
return head;
}
注意:在调用方法的时候但是我们反应过来,直接solution(head);
然后打印的head指向的链表。。。其实,你懂的,俺是菜鸟。。。。。
好吧这里solution(head)
方法中传递的是head的引用,因此执行完head的时候还是指向原来的第一个也就是现在的最后一个节点。。。
代码如下:
public static Node<Integer> solution(Node<Integer> head1,Node<Integer> head2){
// 判空
if(head1 == null || head2 == null) return null;
// 同一个链表,不用再计算长度
if(head1 == head2) return head1;
// 计算长度差
int length1 = 0;
int length2 = 0;
Node<Integer> temp1 = head1;
Node<Integer> temp2 = head2;
while(temp1.getNext() != null || temp2.getNext() != null){
if(temp1.getNext() != null)
temp1 = temp1.getNext();
else
length2++;
if(temp2.getNext() != null)
temp2 = temp2.getNext();
else
length1++;
}
temp1 = head1;
temp2 = head2;
// 链表1长,链表1先走length1步
if(length1 != 0){
// 先执行length1步
for(int i = 0;i<length1;i++){
temp1 = temp1.getNext();
}
while (temp1 != null){
// 相等即为第一个公共节点
if(temp1 == temp2){
return temp1;
}
else{
temp1 = temp1.getNext();
temp2 = temp2.getNext();
}
}
}
// 链表2长,链表2先走length2步
else if(length2 != 0){
// 先执行length2步
for(int i = 0;i<length2;i++){
temp2 = temp2.getNext();
}
while (temp1 != null){
// 相等即为第一个公共节点
if(temp1 == temp2){
return temp1;
}
else{
temp1 = temp1.getNext();
temp2 = temp2.getNext();
}
}
}
// 一样长
else{
while (temp1 != null){
if(temp1 == temp2){
return temp1;
}
else{
temp1 = temp1.getNext();
temp2 = temp2.getNext();
}
}
}
return null;
}
或求环的长度,或求节点总个数。代码如下:
public static Node<Integer> solution(Node<Integer> head){
// 定义两个节点变量,一个每次走两步,一个每次走一步
Node<Integer> aheadNode = head;
Node<Integer> behindNode = head;
// 找到第一次相遇的地方
while(aheadNode.getNext() != null){
aheadNode = aheadNode.getNext().getNext();
behindNode = behindNode.getNext();
if(behindNode == aheadNode) break;
}
// 慢节点指向head,然后两个指针每次走一步
behindNode = head;
while(behindNode != aheadNode){
behindNode = behindNode.getNext();
aheadNode = aheadNode.getNext();
}
return behindNode;
}
衍生问题:
/**
* 判断链表是否有环
* 思路:快慢指针实现,若有环肯定会相交。
**/
public static boolean checkCircuit(Node<Integer> head){
// 判空
if(head == null) return false;
// 定义两个节点,一个快节点,一个慢节点
Node<Integer> aheadNode = head;
Node<Integer> behindNode = head;
// 绝对不能一次判断两个:behindNode.getNext().getNext()!= null
while(behindNode.getNext() != null){
behindNode = behindNode.getNext().getNext();
if(behindNode == null) return false;
aheadNode = aheadNode.getNext();
if(behindNode == aheadNode) return true;
}
return false;
}
一个指针a每次走1步,另一个指针b每次走两步。没有环肯定追不上。
现在考虑有环的情况,存在以下三种情况:
显然,第三种情况自然证明了有环,第1和第2种情况见下图。
具体见下图:
两种实现,递归和非递归。
这题有两种,如下
public static Node<Integer> solution(Node<Integer> head){
// 判空
if(head == null) return head;
Node<Integer> ahead = head;
Node<Integer> curr = head.getNext();
while(curr != null){
if(ahead.getElement() == curr.getElement()) //相同就删除
ahead.setNext(curr.getNext());
else
ahead = curr;//不同后移
curr = curr.getNext();//无论相同与否当前指针都后移
}
return head;
}
public static Node<Integer> solution2(Node<Integer> head){
// 判空
if(head == null) return head;
// 即将判断的节点
Node<Integer> curr = head.getNext();
// curr的前一个节点
Node<Integer> ahead = head;
// 记录ahead的前一个节点
Node<Integer> preAhead = null;
// 记录ahead节点是否有重复
boolean needDelete = false;
while(curr != null){
// 相同就删除
if(ahead.getElement() == curr.getElement()){
ahead.setNext(curr.getNext());
needDelete = true;
}
else{
if(needDelete){
// ahead节点重复需要删除,用后一个节点替换前一个节点,后设置preAhead的后一个节点。
// 若直接设置preAhead的话,开始重复的就不能处理
ahead.setElement(curr.getElement());
ahead.setNext(curr.getNext());
needDelete = false;
}else{
// 不同后移
preAhead = ahead;
ahead = curr;
}
}
// 无论相同与否当前指针都后移
curr = curr.getNext();
}
if(needDelete){
// 若全都相等则返回空
if(preAhead == null){
return null;
}
// 否则就是最后一个节点需要删除
preAhead.setNext(null);
}
return head;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/wangyongge85/article/details/46991815