1.单链表的常见问题:

 *  求单链表中结点的个数: getListLength 
 *  将单链表反转: reverseList(遍历),reverseListRec(递归) 
 *  查找单链表中的倒数第K个结点(k > 0): reGetKthNode 
 *  查找单链表的中间结点: getMiddleNode (快慢指针)
 *  从尾到头打印单链表: reversePrintListStack,reversePrintListRec(递归) 
 *  已知两个单链表pHead1 和pHead2 各自有序,把它们合并成一个链表依然有序: mergeSortedList, mergeSortedListRec 
 *  判断一个单链表中是否有环: hasCycle 
 *  判断两个单链表是否相交: isIntersect 
 *  求两个单链表相交的第一个节点: getFirstCommonNode 
 *  已知一个单链表中存在环,求进入环中的第一个节点: getFirstNodeInCycle, getFirstNodeInCycleHashMap 
 *  给出一单链表头指针pHead和一节点指针pToBeDeleted,O(1)时间复杂度删除节点pToBeDeleted: delete 

import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

/*
*@author: ZhengHaibo
*2014-3-10 Nanjing,njupt,China
*/
/**
* 单链表
*/
public class MySingleLinkedList {
/**
* 定义节点的数据结构
*/
public class Node {
public Node next;//存储下一个节点的地址
public int data;//数据区
}
/**
* 插入节点
* @param head 链表的头
* @param data 要插入的节点数据
* @param i 插入的位置
*/
public boolean insert(Node head,int data,int i){
Node p = head;
int j = 0;
while(p!=null&&j<i-1){
p=p.next;
j++;
}
if(p==null||j>i-1){//i大于表长度+1 或者 i<1
System.out.println("ERROR");
return false;
}
Node node = new Node();
node.data = data;
node.next = p.next;
p.next = node;
return true;
}
/**
* 删除第i个元素,并且将删除后的元素保存在data中
* @param head 链表的头
* @param i 删除元素的位置
* @param data 删除元素的数据区
* @return
*/
public boolean remove(Node head,int i,int data){
Node p = head;
int j = 0;
while(p.next!=null&&j<i-1){//寻到第i个节点,并将p指向其前驱
p=p.next;
j++;
}
if(p.next==null||j>i-1){//删除的位置不合理
return false;
}
data = p.next.data;//将要删除的点的数据保存到data中
p.next = p.next.next;//删除p.next
return true;
}
/**
* 合并2个有序链表(非递减)
* @param head1 待合并的有序链表1
* @param head2 待合并的有序链表2
* @param head 合并后的有序链表
*/
public void mergeList(Node head1,Node head2,Node head){
Node pa = head1.next;
Node pb = head2.next;
Node pc = head;
while(pa!=null&&pb!=null){
if(pa.data<pb.data){
pc.next=pa;
pc=pa;
pa=pa.next;
}else {
pc.next=pb;
pc=pb;
pb=pb.next;
}
}
if(pa==null)pc.next=pb;
if(pb==null)pc.next=pa;
}
/**
* 反转单链表
* 为了反转这个单链表,我们先让头结点的next域指向结点2,
* 再让结点1的next域指向结点3,最后将结点2的next域指向结点1,
* 就完成了第一次交换,顺序就变成了Header-结点2-结点1-结点3-结点4-NULL,
* 然后进行相同的交换将结点3移动到结点2的前面,
* 然后再将结点4移动到结点3的前面,依次将类推
* @param head
*/
public void reverseList(Node head){
if(head==null){
return;
}
Node cur=head.next;
Node p;
while(cur.next!=null){
p=cur.next;
cur.next=p.next;
p.next=head.next;
head.next=p;
}
}
/**
* 借助栈对链表进行翻转
* @param head
*/
public void reverseListStack(Node head){
Stack<Node> stack=new Stack<Node>();
Node pNode=head;
while (pNode.next!=null) {//入栈
pNode=pNode.next;
stack.push(pNode);
}
Node pp=head;
while(!stack.isEmpty()){//出栈
Node node=stack.pop();
pp.next=node;
pp=node;
}
pp.next=null;//防止出现循环链表
}
/**
* 获取链表的长度
* @param head
* @return
*/
public int getListLen(Node head){
Node p=head;
int len=0;
while(p.next!=null){
p=p.next;
len++;
}
return len;
}
/**
* 获取链表中的倒数第K个节点
* 思路1:先获取链表的长度n,然后求取第n+1-k个元素
* @param head
* @param k
* @return
*/
public Node reGetKthNode1(Node head,int k){
int len=0;
Node p=head;
while(p.next!=null){//计算链表长度
len++;
p=p.next;
}
if(k<1||k>len){
return null;
}
Node ppNode=head;
int index=0;
while(ppNode.next!=null){
ppNode=ppNode.next;
index++;
if(index==len+1-k){
return ppNode;
}
}
return null;
}
/**
* 获取链表中的倒数第K个节点
* 思路2:定义2个指针p1和p2,先让p2移动k-1步
* 然后再p1和p2一起移动,直到p2.next==null的时候,
* p1即为倒数第K个元素
* @param head
* @param k
* @return
*/
public Node reGetKthNode2(Node head,int k){
Node p1=head,p2=head;
int step=0;
while(p2.next!=null&&step<k-1){//使p2领先p1 (k-1)步长
p2=p2.next;
step++;
}
if(p2.next==null||step!=k-1){
return null;
}
while (p2.next!=null) {
p1=p1.next;
p2=p2.next;
}
return p1;
}
/**
* 判断链表是否有环
* @param head
* @return
*/
public boolean isLoopList(Node head){
Node p1=head,p2=head;
while(p1!=null&&p2!=null){
p1=p1.next;
p2=p2.next.next;
if(p1==p2){
return true;
}
}
return false;
}

/**
* 在第k个节点制作一个环,使尾部指向第K个节点
* @param head
*/
public boolean makeLoopInKth(Node head,int k){
Node p1=head,p2=head;
while(p1.next!=null){//移动到最后一个节点
p1=p1.next;
}
int index=0;
while(p2.next!=null&&index<k){
p2=p2.next;
index++;
}
p1.next=p2;//将尾部节点指向
return true;
}
/**
* 假设链表中存在环,获取环中的第一个节点
* 如果没有,返回null
* 思路:HashMap中利用key不能重复的特点
* @param head
* @return
*/
public Node getFirstLoopNode(Node head){
Node p=head;
Map<Node,String> map=new HashMap<Node,String>();
while(p.next!=null){
p=p.next;
if(map.get(p)==null){//如果map中还没有这个节点,将其put进入
map.put(p, "1");
}else {
return p;
}
}
return null;
}
/**
* 在两个链表中制作一个相交点
* 在head2的倒数第二个节点制作一个相交点
* @param head1
* @param head2
*/
public void makeCrossingNode(Node head1,Node head2){
Node p1=head1;
Node p2=head2;
while(p1.next!=null){
p1=p1.next;
}
while(p2.next.next!=null){
p2=p2.next;
}
p1.next=p2;
}
/**
* 判断2个链表是否相交
* 思路:遍历两个链表,看最后一个节点是否相同
* @param head1
* @param head2
* @return
*/
public boolean isListCrossing(Node head1,Node head2){
Node p1=head1;
Node p2=head2;
while(p1.next!=null){
p1=p1.next;
}
while(p2.next!=null){
p2=p2.next;
}
return p1==p2;
}
/**
* 获取两条相交链表的第一个公共节点
* 思路1:HashMap 缺点:增加了空间复杂度
* 思路2:
* 对第一个链表遍历,计算长度len1,同时保存最后一个节点的地址。
* 对第二个链表遍历,计算长度len2,同时检查最后一个节点是否和
* 第一个链表的最后一个节点相同,若不相同,不相交,结束。
* 两个链表均从头节点开始,假设len1大于len2
* ,那么将第一个链表先遍历len1-len2个节点,此时两个链表当前
* 节点到第一个相交节点的距离就相等了,然后一起向后遍历,直到
* 两个节点的地址相同。
* 时间复杂度,O(len1+len2)
* @param head1
* @param head2
* @return
*/
public Node getFristCrossingNode(Node head1,Node head2){
Node p1=head1;
Node p2=head2;
int len1=0,len2=0;
while(p1.next!=null){
len1++;
p1=p1.next;
}
while(p2.next!=null){
len2++;
p2=p2.next;
}
if(p2!=p1){//说明不相交
return null;
}
Node pp1=head1;
Node pp2=head2;
int step;//计算长的链表比短的链表长了多少
if(len1>=len2){
step=len1-len2;
for(int i=0;i<step;i++){
pp1=pp1.next;
}
}else {
step=len2-len1;
for(int i=0;i<step;i++){
pp2=pp2.next;
}
}
while(pp1!=pp2){
pp1=pp1.next;
pp2=pp2.next;
}
return pp1;
}
/**
* 打印链表
* @param head
*/
public void displayList(Node head){
Node p = head;
while(p.next!=null){
p=p.next;
System.out.println(p.data);
}
}
/**
* 打印链表
* @param head
* @param len 打印长度不超过len
*/
public void displayList(Node head,int len){
Node p = head;
int index=0;
while(p.next!=null&&index<len){
p=p.next;
index++;
System.out.println(p.data);
}
}

public void test(){
// Node headNode = new Node();
// int []datas = {32,45,8,2,1,4,5,87,57};
// for(int i=0;i<datas.length;i++){//依次将数据插入到链表中
// insert(headNode, datas[i],i+1);
// }
// int removeData=0;
// displayList(headNode);
// remove(headNode,4,removeData);
// System.out.println("-------删除之后---------");
// displayList(headNode);
///
// Node headNode1 = new Node();
// Node headNode2 = new Node();
// Node node3 = new Node();
// int []datas1 = {1,3,5,7,9,11};
// int []datas2 = {2,3,6,8,12};
// for(int i=0;i<datas1.length;i++){//依次将数据插入到链表中
// insert(headNode1, datas1[i],i+1);
// }
// for(int i=0;i<datas2.length;i++){//依次将数据插入到链表中
// insert(headNode2, datas2[i],i+1);
// }
// mergeList(headNode1, headNode2, node3);
// displayList(node3);

Node headNode = new Node();
int []datas = {32,45,8,2,1,4,5,87,57};
for(int i=0;i<datas.length;i++){//依次将数据插入到链表中
insert(headNode, datas[i],i+1);
}
//reverseList(headNode);
//reverseListStack(headNode);
//displayList(headNode);
//System.out.println(getListLen(headNode));
//System.out.println(reGetKthNode2(headNode,2).data);
//System.out.println(isLoopList(headNode));
//makeLoopInKth(headNode, 4);//在第4个节点,人为形成一个环
//displayList(headNode, 30);
//System.out.println(isLoopList(headNode));
//Node node=getFirstLoopNode(headNode);
//if(node!=null){
// System.out.println(node.data);
//}
Node headNode2 = new Node();
int []datas2 = {9,8,7,6,51};
for(int i=0;i<datas2.length;i++){//依次将数据插入到链表中
insert(headNode2, datas2[i],i+1);
}
makeCrossingNode(headNode, headNode2);//人为制作一个相交点
System.out.println(isListCrossing(headNode, headNode2));
Node node2=getFristCrossingNode(headNode, headNode2);
System.out.println(node2.data);
}
public static void main(String[] args) {
new MySingleLinkedList().test();
}
}


~使用归并排序算法,对单链表进行排序

/*
* $filename: SortLinkedList.java,v $
* $Date: 2014-9-7 $
* Copyright (C) ZhengHaibo, Inc. All rights reserved.
* This software is Made by Zhenghaibo.
*/
package edu.njupt.zhb;
/*
*@author: ZhengHaibo
*web: http://www.mobctrl.net
*2014-9-7 Nanjing,njupt,China
*/
public class SortLinkedList {
public class ListNode {
public int val;
public ListNode next;

public ListNode(int x) {
val = x;
next = null;
}
}
// merge sort
public ListNode mergeSortList(ListNode head) {

if (head == null || head.next == null)
return head;

// count total number of elements
int count = 0;
ListNode p = head;
while (p != null) {
count++;
p = p.next;
}

// break up to two list
int middle = count / 2;

ListNode l = head, r = null;
ListNode p2 = head;
int countHalf = 0;
while (p2 != null) {
countHalf++;
ListNode next = p2.next;

if (countHalf == middle) {
p2.next = null;
r = next;
}
p2 = next;
}

// now we have two parts l and r, recursively sort them
ListNode h1 = mergeSortList(l);
ListNode h2 = mergeSortList(r);

// merge together
ListNode merged = merge(h1, h2);

return merged;
}

public ListNode merge(ListNode l, ListNode r) {
ListNode p1 = l;
ListNode p2 = r;

ListNode fakeHead = new ListNode(100);
ListNode pNew = fakeHead;

while (p1 != null || p2 != null) {

if (p1 == null) {
pNew.next = new ListNode(p2.val);
p2 = p2.next;
pNew = pNew.next;
} else if (p2 == null) {
pNew.next = new ListNode(p1.val);
p1 = p1.next;
pNew = pNew.next;
} else {
if (p1.val < p2.val) {
// if(fakeHead)
pNew.next = new ListNode(p1.val);
p1 = p1.next;
pNew = pNew.next;
} else if (p1.val == p2.val) {
pNew.next = new ListNode(p1.val);
pNew.next.next = new ListNode(p1.val);
pNew = pNew.next.next;
p1 = p1.next;
p2 = p2.next;

} else {
pNew.next = new ListNode(p2.val);
p2 = p2.next;
pNew = pNew.next;
}
}
}
return fakeHead.next;
}

public void test(){
ListNode n1 = new ListNode(2);
ListNode n2 = new ListNode(8);
ListNode n3 = new ListNode(4);

ListNode n4 = new ListNode(5);
ListNode n5 = new ListNode(4);
ListNode n6 = new ListNode(9);

n1.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n5;
n5.next = n6;

n1 = mergeSortList(n1);

printList(n1);
}

public static void main(String[] args) {
new SortLinkedList().test();
}

public void printList(ListNode x) {
if(x != null){
System.out.print(x.val + " ");
while (x.next != null) {
System.out.print(x.next.val + " ");
x = x.next;
}
System.out.println();
}

}
}


2.附录:

重温数据结构:单链表的常见问题总结_链表


重温数据结构:单链表的常见问题总结_链表_02

重温数据结构:单链表的常见问题总结_i++_03


重温数据结构:单链表的常见问题总结_i++_04


图3-9在单链表中删除节点