/**
* 链表节点
*/
class Node{
Node next=null;
int data;
public Node(int data){
this.data=data;
}
}
import java.util.*;
public class MyLinkedList {
Node head=null;//链表头
/**
* 向链表中插入数据
* @param d
*/
public void addNode(int d){
Node newNode=new Node(d);
if(head==null){
head=newNode;
return ;
}
Node temp=head;
while(temp.next!=null){
temp=temp.next;
}
//在链表末尾插入新节点
temp.next=newNode;
}
/**
* 删除链表的第index个节点
* 如成功则返回true,否者返回false
* @param index
* @return
*/
public boolean deleteNode(int index){
if(index<1 || index>length()){
return false;
}
Node preNode=head;
Node curNode=preNode.next;
int i=1;
while(curNode!=null){
if(i==index){
preNode.next=curNode.next;
//curNode.next=null;该句不能加
return true;
}else{
i++;
preNode=curNode;
curNode=curNode.next;
}
}
return true;
}
/**
* 返回链表的长度
* @return
*/
public int length(){
Node temp=head;
int length=0;
while(temp!=null){
length++;
temp=temp.next;
}
return length;
}
/**
* 对链表进行排序(冒泡排序)
* 返回排序后的头节点
* @return
*/
public Node orderList(){
if(head==null) return head;
Node curNode=head;
Node nextNode=null;
int temp=0;
while(curNode.next!=null){
nextNode=curNode.next;
while(nextNode!=null){
if(curNode.data>nextNode.data){
temp=curNode.data;
curNode.data=nextNode.data;
nextNode.data=temp;
}
nextNode=nextNode.next;
}
curNode=curNode.next;
}
return head;
}
/**
* 删除链表中重复数据
* 方法1:遍历链表,把遍历到的值存储在一个Hashtable中,在遍历过程中,若当前访问的值在Hashtable中已经存在,则删除。
* 方法2:对链表进行双重循环遍历,外循环正常遍历链表,假设外循环当前遍历的节点为cur,内循环从cur开始遍历,
* 若碰到与cur所指向节点值相同,则删除这个重复节点
* 注:方法1时间复杂度较低,但需要额外的空间来保存已经遍历过的值;方法2不需要额外空间,但是时间复杂度较高。
*/
public void deleteDuplecate1(Node head){
Hashtable<Integer,Integer> table=new Hashtable<>();
Node temp=head;
Node pre=null;
while(temp!=null){
if(table.containsKey(temp.data)){
pre.next=temp.next;
}else{
table.put(temp.data, 1);
pre=temp;
}
temp=temp.next;
}
}
public void deleteDuplecate2(Node head){
Node p=head;
Node q=null;
while(p!=null){//此段代码比较精妙
q=p;
while(q.next!=null){
if(p.data==q.next.data){
q.next=q.next.next;
}else{
q=q.next;
}
}
p=p.next;
}
}
/**
* 找出单链表中的倒数第k个元素
* 方法1:先遍历一遍单链表,求出整个但链表的长度n,然后倒数第k个转换为整数第n-k个,接下去遍历一次就可以得到结果
* 方法2:从头节点开始,一次对链表中的每一个元素进行这样的测试:遍历k个元素,查看是否到达链表尾,直到找到那个倒数第k个数。
* 方法3:设置两个指针,让其中一个指针比另一个指针先前期k-1步,然后两个指针同时向前移动,
* 循环直到先行的指针值为null时,另一个指针所指位置就是要找的位置。
* 方法1和方法2时间复杂度较高,建议使用方法3
*/
public Node findElem(Node head,int k){
if(k<1 || k>length()){
return null;
}
Node p1=head;
Node p2=head;
for(int i=1;i<k;i++){//p2前移k-1步
p2=p2.next;
}
while(p2!=null){
p1=p1.next;
p2=p2.next;
}
return p1;
}
/**
* 反转链表
*/
public void reverseIteratively(Node head){
Node pre=null;
Node cur=head;
Node next=head;
while(cur!=null){
next=cur.next;
cur.next=pre;
pre=cur;
cur=next;
}
head=pre;
}
/**
* 从尾到头输出单链表
* 方法1:反转后输出。
* 方法2:栈。
* 方法3:递归(利用栈的形式)
*/
public void printListReversely(Node head){
if(head!=null){
printListReversely(head.next);
System.out.println(head.data);
}
}
/**
* 寻找单链表的中间节点
* 方法1:先遍历一遍链表,求出整个链表的长度n,然后再从头指针开始前移n/2步到达中间节点
* 方法2:快慢指针
*/
public Node searchMid(Node head){
Node fast=head;
Node slow=head;
while(fast!=null && fast.next!=null){
fast=fast.next.next;
slow=slow.next;
}
return slow;
}
/**
* 如何检测一个链表是否有环
* 方法:快慢指针思想:定义两个指针,其中fast是快指针,slow是慢指针,二者的初始值都指向链表头;
* slow每次前进一步,fast每次前进两步,两个指针同时向前移动,快指针每移动一次都要跟慢指针比较,
* 直到当快指针等于慢指针为止,就证明这个链表是带环的单向链表。
*/
public boolean isLoop(Node head){
Node fast=head;
Node slow=head;
while(fast!=null && fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(fast==slow){
return true;
}
}
return false;
}
/**
* 若链表有环,如何找到环的入口?
*/
public Node findLoopPort(Node head){
Node fast=head;
Node slow=head;
while(fast!=null && fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(fast==slow) break;
}
if(fast==null || fast.next==null){
return null;
}
slow=head;
while(fast!=slow){
fast=fast.next;
slow=slow.next;
}
return slow;
}
/**
* 如何在不知道头指针的情况下删除指定节点
* 可以分为两种情况:
* 1.若待删除节点为链表尾节点,则无法删除,因为删除后无法使其前驱节点的next指针置为空;
* 2.若待删除节点不是链表尾节点,则可以通过交换这个节点与其后继节点的值,然后删除后继节点。
*/
public boolean deleteNode(Node n){
if(n==null || n.next==null){
return false;
}
int temp=n.data;
n.data=n.next.data;
n.next.data=temp;
n.next=n.next.next;
return true;
}
/**
* 如何判断两个链表是否相交
* 方法:如果两个链表相交,那么他们一定有着相同的尾节点;分别遍历两个链表,记录它们的尾节点,
* 如果它们的尾节点相同,那么这两个链表相交。
*/
public boolean isIntersect(Node h1,Node h2){
if(h1==null || h2==null){
return false;
}
Node tail1=h1;
//找到链表1的最后一个节点
while(tail1.next!=null){
tail1=tail1.next;
}
Node tail2=h2;
//找到链表2的最后一个节点
while(tail2.next!=null){
tail2=tail2.next;
}
return tail1==tail2;
}
/**
* 如果两个链表相交,如何找到它们相交的第一个节点?
* 方法:首先分别计算两个链表h1,h2的长度len1,len2(假设len1>len2),接着先对链表h1遍历(len1-len2)个节点
* 到节点p,此时节点p与它们相交的节点的距离相同,此时同时遍历两个链表,直到遇到相同的两个节点为止,这个节点就是它们相交的节点。
* 需要注意的是,在查找相交的第一个节点之前,需要先判断两个链表是否相交。
*/
public Node getFirstMeetNode(Node h1,Node h2){
if(h1==null || h2==null){
return null;
}
Node tail1=h1;
int len1=1;
//找到链表1的最后一个节点
while(tail1.next!=null){
tail1=tail1.next;
len1++;
}
Node tail2=h2;
int len2=1;
//找到链表2的最后一个节点
while(tail2.next!=null){
tail2=tail2.next;
len2++;
}
//两表不相交
if(tail1!=tail2){
return null;
}
Node t1=h1;
Node t2=h2;
//找出较长的表,先遍历
if(len1>len2){
int d=len1-len2;
while(d>0){
t1=t1.next;
d--;
}
}else{
int d=len2-len1;
while(d>0){
t2=t2.next;
d--;
}
}
while(t1!=t2){
t1=t1.next;
t2=t2.next;
}
return t1;
}
}
此博客内容来自《Java程序员面试笔试宝典》