鏈表的各種操作java版本__java
最後更新:2018-07-27
來源:互聯網
上載者:User
package singleList;
import java.util.Stack;
/*
* yy
* 1:單鏈表的插入從頭開始進行插入
* 2:查詢某個元素
* 3:刪除某個元素
* 4:尋找單鏈表的中間結點快速的方法
* 5:將單鏈表逆轉
* 6:將鏈表倒置輸出
* 7:單鏈表的長度是多少
* 8:合并兩個有序的單鏈表head1和head2,迴圈
* 9:判斷一個單鏈表中是否有環
* 10:找到環的進入點
* 11:帶環鏈表的長度
* 12:如何知道環的長度
* */
//單鏈表的節點
class Node<T extends Comparable<T>> implements Comparable<Node<T>>{
private T data;
private Node<T> next;
public Node(){
}
public Node(T data){
this.data=data;
this.next=null;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Node<T> getNext() {
return next;
}
public void setNext(Node<T> next) {
this.next = next;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((data == null) ? 0 : data.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Node other = (Node) obj;
if (data == null) {
if (other.data != null)
return false;
} else if (!data.equals(other.data))
return false;
return true;
}
@Override
public int compareTo(Node<T> o) {
return this.data.compareTo(o.getData());
}
}
/*
* 這個單鏈表是沒有哨兵的
* 在下一個版本中我會加入哨兵
* */
public class SingleList<T extends Comparable<T>> {
private Node<T> head;
public SingleList(){
head=null;
}
public Node<T> getHead() {
return head;
}
//單鏈表的插入從頭開始進行插入
public void insertData(T data){
Node<T> node=new Node<T>(data);
if(this.head==null){
this.head=node;
}else{
node.setNext(head);
head=node;
}
}
//查詢某個元素
public Node<T> search(T data){
Node<T> result=this.search(this.head, new Node(data));
return result==null?null:result;
}
private Node<T> search(Node<T> head,Node<T> element){
Node<T> node=head;
while(node!=null){
if(node.equals(element)){
return node;
}
node=node.getNext();
}
return null;
}
/*
* 刪除某個元素
* 如果存在則返回true 否則返回false
* */
public boolean deleteData(T data){
return this.deleteData(this.head, new Node<T>(data));
}
private boolean deleteData(Node<T> head,Node<T> element){
Node<T> node=head;
Node<T> pre=null;
while(node!=null){
if(node.equals(element)){
if(node.equals(head)){
this.head=node.getNext();
}else{
pre.setNext(node.getNext());
}
node=null;
return true;
}
pre=node;
node=node.getNext();
}
return false;
}
//尋找單鏈表的中間結點快速的方法
/*
* 鏈表為空白;
鏈表非空,但僅有一個或兩個節點;可以直接返回第一個節點的元素值。
鏈表非空,但含有三個或三個以上的節點,可以通過定義兩個指標,一個指標的跳步為2次的時候,另一個指標的跳步為1次,當跳至結尾時,另一個節點恰好在中間位置。
* */
public T getMiddleNode(){
Node<T> res=this.getMiddleNode(this.head);
return res==null?null:res.getData();
}
private Node<T> getMiddleNode(Node<T> head){
Node<T> node=head;
Node<T> halfnode=null;
if(node==null){
return null;
}else if(node.getNext()==null||node.getNext().getNext()==null){
return node;
}else{
halfnode=node;
while(node.getNext()!=null){
node=node.getNext();
if(node.getNext()!=null){
node=node.getNext();
halfnode=halfnode.getNext();
}
}
return halfnode;
}
}
//將單鏈表逆轉
public void inverse(){
this.inverse(this.head);
}
private void inverse(Node<T> head){
Node<T> node=head.getNext();
Node<T> pnode=null;
this.head.setNext(null);
while(node!=null){
pnode=node;
node=node.getNext();
pnode.setNext(this.head);
this.head=pnode;
}
}
//將鏈表倒置輸出
public void inverseTraverse(){
this.inverseTraverse(this.head);
}
private void inverseTraverse(Node<T> head){
Node<T> node=head;
Stack<Node<T>> stack=new Stack<Node<T>>();
while(node!=null){
stack.push(node);
node=node.getNext();
}
while(!stack.isEmpty()){
System.out.print(stack.pop().getData()+" ");
}
System.out.println();
}
//單鏈表的遍曆
public void travense(){
travense(this.head);
}
private void travense(Node<T> head){
Node<T> node=head;
while(node!=null){
System.out.print(node.getData()+" ");
node=node.getNext();
}
}
//單鏈表的長度是多少
public int size(){
return size(this.head);
}
private int size(Node<T> head){
Node<T> node=head;
int count=0;
while(node!=null){
count++;
node=node.getNext();
}
return count;
}
//合并兩個有序的單鏈表head1和head2,迴圈
public Node<T> mergeSortedList(Node<T> head1,Node<T> head2){
if(head1==null)return head2;
if(head2==null)return head1;
Node<T> target=null;
if(head1.getData().compareTo(head2.getData())<0){
target=head1;
head1=head1.getNext();
}else{
target=head2;
head2=head2.getNext();
}
target.setNext(null);
Node<T> mergeHead=target;//記錄串連後的頭指標
while(head1!=null&&head2!=null){
if(head1.getData().compareTo(head2.getData())<0){
target.setNext(head1);
head1=head1.getNext();
}else{
target.setNext(head2);
head2=head2.getNext();
}
target=target.getNext();
target.setNext(null);
}
if(head1==null){
target.setNext(head2);
}else{
target.setNext(head1);
}
return mergeHead;
}
public void mergeTraverse(Node<T> node){
this.travense(node);
}
// 判斷一個單鏈表中是否有環: hasCycle
/*
* 設定兩個指標(fast, slow),初始值都指向頭,slow每次前進一步,fast每次前進二步,
* 如果鏈表存在環,則fast必定先進入環,而slow後進入環,兩個指標必定相遇。
* (當然,fast先行頭到尾部為NULL,則為無環鏈表)
* */
public boolean hasCycle(){
return this.hasCycle(this.head);
}
private boolean hasCycle(Node<T> head){
boolean flag=false;
Node<T> slow=head;
Node<T> fast=head;
while(fast!=null&&fast.getNext()!=null){
slow=slow.getNext();
fast=fast.getNext().getNext();
if(slow.equals(fast)){
flag=true;
break;
}
}
return flag;
}
/*
* 找到環的進入點
* 假設單鏈表的總長度為L,頭結點到環入口的距離為a,環入口到快慢指標相遇的結點距離為x,環的長度為r,
* 慢指標總共走了s步,則快指標走了2s步。
* 另外,快指標要追上慢指標的話快指標至少要在環裡面轉了一圈多(假設轉了n圈加x的距離),得到以下關係:
s = a + x;
2s = a + nr + x;
=>a + x = nr;
=>a = nr - x;
*/
//尋找環的進入點
private Node<T> FindLoopPort(Node<T> head)
{
Node<T> slow=head,fast=head;
//得到相遇點
while(fast!=null && fast.getNext()!=null)
{
slow=slow.getNext();
fast=fast.getNext().getNext();
if(slow.equals(fast))
break;
}
if(fast==null||fast.getNext()==null)
return null; //無環
//slow指向開頭,fast在相遇點
//得到進入點
slow=head;
while(!slow.equals(fast)){
slow=slow.getNext();
fast=fast.getNext();
}
return slow;
}
/*
* 如何知道環的長度
* 記錄下問題1的碰撞點p,slow、fast從該點開始,再次碰撞所走過的運算元就是環的長度s。
* */
public int getCycleLength(){
return this.getCycleLength(this.head);
}
private int getCycleLength(Node<T> head){
Node<T> p=this.FindLoopPort(head);
if(p==null) return 0;//無環
Node<T> slow=p;
Node<T> fast=p;
int count=0;
while(fast!=null&& fast.getNext()!=null){
slow=slow.getNext();
fast=fast.getNext().getNext();
count++;
if(slow.equals(fast)){
break;
}
}
return count;
}
//帶環鏈表的長度
public int getCycleListLength(){
return getCycleListLength(this.head);
}
private int getCycleListLength(Node<T> head){
Node<T> pnode=this.FindLoopPort(head);
if(pnode==null){
return this.size(head);
}
int count=0;//記錄頭結點到存取點的距離
Node<T> node=head;
while(!node.equals(pnode)){
count++;
node=node.getNext();
}
int count1=this.getCycleLength(head);//得到環的長度
return count+count1;
}
// 判斷兩個單鏈表是否相交
//如果單純的判斷是否相交,只需要看最後一個指標是否相等
/*
* 一共有二中解法
* 第一種:將二個鏈表串連起來,查看鏈表2是否存在環。如果存在環,則第一個進入點就是相交的點
*
* */
//單鏈表是沒有環的
/*
* 二個鏈表相交的性質
* (1)一旦兩個鏈表相交,那麼兩個鏈表中的節點一定有相同地址。
(2)一旦兩個鏈表相交,那麼兩個鏈表從相交節點開始到尾節點一定都是相同的節點。
* 解題思路
* 先遍曆第一個鏈表到他的尾部,然後將尾部的next指標指向第二個鏈表(尾部指標的next本來指向的是null)。
* 這樣兩個鏈表就合成了一個鏈表,判斷原來的兩個鏈表是否相交也就轉變成了判斷新的鏈表是否有環的問題了:即判斷單鏈表是否有環
* */
public Node<T> isIntersectByConnect(Node<T> head1,Node<T> head2){
//得到當前鏈表的最後一個節點
Node<T> target=null;
if(head1!=null){
while(head1.getNext()!=null){
head1=head1.getNext();
}
head1.setNext(head2);
target=head1;
}else if(head2!=null){
while(head2.getNext()!=null){
head2=head2.getNext();
}
head2.setNext(head1);
target=head2;
}
return this.FindLoopPort(head2);
}
public Node<T> isIntersect(Node<T> head1,Node<T> head2){
Node<T> target=null;
if(head1==null||head2==null)return target;
boolean pos1=this.hasCycle(head1);
boolean pos2=this.hasCycle(head2);
// 一個鏈表有環,另一個鏈表沒環,那肯定沒有交點
if(pos1==false&&pos2==true||pos1==false&&pos2==true)return null;
int len1=this.size(head1);
int len2=this.size(head2);
if(len1>=len2){
for(int i=0;i<len1-len2;i++){
head1=head1.getNext();
}
}else{
for(int i=0;i<len2-len1;i++){
head2=head2.getNext();
}
}
while(head1!=null&&head2!=null){
if(head1.equals(head2)){
target=head1;
break;
}
else{
head1=head1.getNext();
head2=head2.getNext();
}
}