1.实现单链表反转
有四种方法,代码如下:
package linkedlist;
import java.util.Stack;
public class ReverseIteratively {
/*
* 方法一:存储为数组,按照数组的索引逆序进行反转
* 或者利用栈
*/
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.isEmpty()){
System.out.println(stack.pop().data+" ");
}
}
/*
* 方法二:使用三个指针遍历单链表,逐个链接点进行反转
*/
public static Node reverseIteratively(Node head){
if(head==null){
return head;
}
Node p=new Node();
Node q=new Node();
Node r=new Node();
p=head;
q=head.next;
head.next=null;
while(q!=null){
r=q.next;
q.next=p;
p=q;
q=r;
}
head=p;
return head;
}
/*
* 方法三:从第二个结点到第n个结点,逐结点插入到第一个结点之后
* 最后将第一个结点挪到新表的表尾
*/
public static Node reverse(Node head){
Node p=new Node();
Node q=new Node();
p=head.next;
while(p.next!=null){
q=p.next;
p.next=q.next;
q.next=head.next;
head.next=q;
}
p.next=head;
head=p.next.next;
p.next.next=null;
return head;
}
/*
* 方法四:递归,和从尾到头输出单链表相似
*/
public static Node printListReversely(Node pListHead){
// if(pListHead!=null){
// printListReversely(pListHead.next);
// System.out.println(pListHead.data);
// }
if(pListHead==null||pListHead.next==null){
return pListHead;
}
Node rehead=printListReversely(pListHead.next);
pListHead.next.next=pListHead;//将头结点置于末端
pListHead.next=null;//防止链表循环
return rehead;
}
public static void print(Node head){
while(head!=null){
System.out.print(head.data+" ");
head=head.next;
}
System.out.println();
}
public static void main(String [] args){
Node node1=new Node(2);
Node node2=new Node(4);
Node node3=new Node(1);
node1.next=node2;
node2.next=node3;
// Node head=reverseIteratively(node1);
// Node head=reverse(node1);
Node head=printListReversely(node1);
// reversePrint(node1);
print(head);
// printListReversely(node1);
}
}
(http://blog.csdn.net/feliciafay/article/details/6841115)为了更好地理解方法二,以下图片参考自上述网址:
2.寻找单链表的中间结点
package linkedlist;
/*
* 有两个指针同时从头开始遍历
* 一个快指针一次走两步,一个慢指针一次走一步
* 快指针先到链表尾部,而慢指针恰好到达链表中部
* (快指针到达链表尾部,链表长度为奇数时,慢指针指向的即是链表中间指针;
* 链表长度为偶数时,慢指针指向的结点和慢指针指向的下一个结点都是链表的中间结点)
*/
public class SearchMid {
public static Node searchMid(Node head){
Node p=head;//快指针
Node q=head;//慢指针
while(p!=null && p.next!=null &&p.next.next!=null){
p=p.next.next;
q=q.next;
}
return q;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Node node1=new Node(2);
Node node2=new Node(4);
Node node3=new Node(1);
Node node4=new Node(3);
node1.next=node2;
node2.next=node3;
node3.next=node4;
Node mid=searchMid(node1);
System.out.println(mid.data);
}
}
3.如何检测一个链表是否有环,并找到环的入口点
如果单链表有环,当fast与slow相遇时,slow指针肯定没有遍历完链表,而fast指针已经在环内循环了n圈,假设slow指针走了s步,则fast指针走了2s,设环长为r则满足:
2s=s+nr
s=nr
设整个链表长L,入口处于像雨点距离为x,起点到环入口点距离为a则:
a+x=nr
a+x=(n-1)r+r=(n-1)+L-a
a=(n-1)+(L-a-x)
即从链表起点head到入口点距离a,与从slow和fast的相遇点到入口点的距离相等,于是在链表头和相遇点分别设一个指针,每次走一步,两个指针必定相遇,并且相遇第一点即为环入口点:
package linkedlist;
public class IsLoop {
/*
* 定义两个指针fast和slow,二者的初始值都是指向链表头
* slow每次前进一步,fast每次前进两步,两个指针同时向前移动
* 快指针每移动一次都要和慢指针进行比较,直到当快指针等于慢指针为止,就证明这个链表是带环的
* 否则证明该链表是不带环的循环链表(fast先行到达尾部为NUll,则为无环链表)
*/
public static boolean isLoop(Node head){
Node fast=head;
Node slow=head;
if(fast==null){
return false;
}
while(fast!=null && fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(fast==slow){
return true;
}
}
return !(fast==null || fast.next==null);
}
/*
* 找到环的入口点
*/
public static Node findLoopPort(Node head){
Node slow=head;
Node fast=head;
while(fast!=null && fast.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast){//相遇点
break;
}
}
if(fast==null||fast.next==null){
return null;
}
slow=head;
while(slow!=fast){
slow=slow.next;
fast=fast.next;
}
return slow;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Node node1=new Node(2);
Node node2=new Node(4);
Node node3=new Node(1);
Node node4=new Node(3);
Node node5=new Node(5);
Node node6=new Node(7);
node1.next=node2;
node2.next=node3;
// node3.next=node1;
node3.next=node4;
node4.next=node5;
node5.next=node6;
node6.next=node3;
System.out.println(isLoop(node1));
System.out.println(findLoopPort(node1).data);
}
}
4.在不知道头指针的情况下删除指定结点
package linkedlist;
/*
* 1.若待删除的结点为链表尾结点,则无法删除,因为删除后无法使其前驱节点的next指针置为空
* 2.若待删除的结点不是链表尾结点,可以通过交换这个结点与其后继结点的值,然后删除后继结点
*/
public class DeleteNode {
public static boolean deleteNode(Node n){
if(n==null||n.next==null){
return false;
}
int tmp=n.data;
n.data=n.next.data;
n.next.data=tmp;
n.next=n.next.next;
return true;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Node node1=new Node(2);
Node node2=new Node(1);
Node node3=new Node(6);
node1.next=node2;
node2.next=node3;
System.out.println(deleteNode(node3));
}
}
5.判断两个链表是否相交
链表,只有一个指向下一个结点的指针,所以只有汇聚,没有交叉,如图所示:
package linkedlist;
public class IsIntersect {
/*
* 两个链表相交,它们一定有着相同的尾结点
* 思路:分别遍历两个链表,记录它们的尾结点,如果相同则链表相交
* 算法时间复杂度为O(len1+len2)
*/
public static boolean isIntersect(Node h1,Node h2){
if(h1==null||h2==null){
return false;
}
Node tail1=h1;
while(tail1.next!=null){
tail1=tail1.next;
}
Node tail2=h2;
while(tail2.next!=null){
tail2=tail2.next;
}
return tail1==tail2;
}
/*
* 如果两个链表相交,如何找到相交的第一个结点
* 思路:分别计算两个链表的长度len1和len2(设len1>len2)
* 先对链表h1遍历(len1-len2)个结点到p,此时p和h2到它们相交的结点的距离相等
* 同时遍历这两个链表,直到遇到相同的结点为止
*/
public static Node getFirstMeetNode(Node h1,Node h2){
if(h1==null||h2==null){
return null;
}
Node tail1=h1;
int length1=0;
while(tail1.next!=null){
tail1=tail1.next;
length1++;
}
Node tail2=h2;
int length2=0;
while(tail2.next!=null){
tail2=tail2.next;
length2++;
}
if(tail1!=tail2){
return null;
}
Node t1=h1;
Node t2=h2;
if(length1>=length2){
int d=length1-length2;
while(d!=0){
t1=t1.next;
d--;
}
}
else{
int d=length2-length1;
while(d!=0){
t2=t2.next;
d--;
}
}
while(t1!=t2){
t1=t1.next;
t2=t2.next;
}
return t1;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Node node1=new Node(2);
Node node2=new Node(1);
Node node3=new Node(9);
Node node4=new Node(6);
Node node5=new Node(7);
Node node6=new Node(3);
Node node7=new Node(5);
node1.next=node2;
node2.next=node6;
node3.next=node4;
node4.next=node5;
node5.next=node6;
node6.next=node7;
boolean result=isIntersect(node1,node3);
System.out.println(result);
Node firstMeet=getFirstMeetNode(node1,node3);
System.out.println(firstMeet.data);
}
}