JAVA寫一個單鏈表
1、已知帶頭結點的動態單鏈表 L 中的結點是按整數值遞增排序的,試寫一 算法將值爲 x 的結點插入到表 L 中,使 L 仍然有序。要求算法的時間複雜度爲 O(n),空間複雜度爲 O(1)。
2、設計一算法,逆置帶頭結點的動態鏈表 L。要求利用原表的結點空間, 並要求用盡可能少的時間完成。
3、假設有兩個按元素值遞增有序的線性表 A 和 B,均以單鏈表作存儲結構, 試編寫算法將 A 表和 B 表歸併成一個按元素值遞減有序的線性表性表 C,並要求 利用原表的空間存放 C,並要求用盡可能少的時間完成。
如何插入一段漂亮的代碼片
- 要求一:不破壞表的順序插入元素,時間複雜度O(n)
一個for循環即可解決
//
public void Insert_order(int e){
Node current = header;
if(header == null) //空表直接尾部加
add(e);//在尾部添加元素
int i;
for(i=0;i<size&¤t!=null;i++,current=current.next){
if(current.data>=e){//如果某個結點的指大於等於輸入值
if(i==0) {//如果是在頭節點,直接加在前面
addHead_e(e);//在頭部添加元素
break;
}
Node p=this.getNodeByIndex(i-1);//指定位置的前一個節點
p.next=new Node(e,p.next);//這是不在首位的情況,獲取當前節點的前一個節點並將e插在它後面,同時新節點的指向是原節點的下一個節點
size++;
break;
}
}
if(i==size)//如果循環沒有被break結束,那麼i的值與size的值相等,
add(e);//這說明這個元素比原鏈表中任何一個都大,直接在尾部添加節點即可
//System.out.println(size);
}
用到的add(e)和addHead_e(e)方法:
//直接在尾部插入元素
public void add(int e){
if(header==null) {
header = new Node(e, null);
tail = header;
}
else{
Node p=new Node(e,null);
tail.next=p;
tail=p;
}
size++;
}
//在表頭插入元素
public void addHead_e(int e){
Node p=new Node(e,null);
p.next=header;
header=p;
if(header.next==null)
tail=header;
size ++;
}
用到的getNodeByIndex(index)方法:
// 獲取指定節點
private Node getNodeByIndex(int index){
if(index < 0 || index >= size)
throw new IndexOutOfBoundsException("索引超出線性表範圍");
Node current = header;//從header開始遍歷
for(int i=0; i<size && current!=null; i++,current=current.next){
if(i == index)
return current;
}
return null;
}
- 要求2:逆置帶頭結點的動態單鏈表 L
一共有三種方法,這裏選擇的是迭代法
事實上這裏可以不需要返回值的,截至到header=p就可以了
具體結合實際應用
//逆置(迭代法)
/*
1.先將當前節點的下一節點記錄下來,
2.然後將當前節點指向上一節點
3.再將當前節點記錄下來,讓記錄的下一節點變成當前節點
* */
public Node ReverseLink1(){
if(header==null||header.next==null)//如果表頭爲空或者當前鏈表只有一個節點,
return header; //那麼直接返回就可以了
Node p,q;
p=null;//p用來保存上一節點,開始時,表頭沒有上一節點(表頭的上一節點爲空)
q=header;//q用來保存當前節點,從表頭開始操作
while(q!=null){//只要當前節點不爲空,就要進行操作
Node r=q.next;////1.r保存當前節點的下一節點
q.next=p;//2.讓當前節點指向上一節點,第一次爲空,第二次開始指向上一節點
p=q;//3.讓上一節點變成當前節點(把當前節點記錄下來,記錄進p裏)
q=r;//3.讓記錄的下一節點變成當前節點
}
header=p;
return header;
}
- 要求三:元素值遞增有序的線性表 A 和 B,以單鏈表作存儲結構,將 A 表和 B 表歸併成一個按元素值遞減有序的線性表性表 C,並要求 利用原表的空間存放 C
-------------------------------------------------------------------------------------也就是
將A和B兩個遞增單鏈表合成遞減單鏈表並要求用原來的存儲空間
這裏我的想法是這樣的:把B表的元素一個一個拿出來,插入到A表中再逆置
/*
兩個遞增鏈表合成一個遞減鏈表
利用前面的函數和輔助節點p,將b鏈表的值按大小順序插入到a表中,
形成包含a和b所有元素的遞增鏈表,再將鏈表用寫好的逆置方法逆置就可以了
* */
public void Combine_order_increase(My_list b) {
Node p,q;
p=this.header;
q=b.header;
while(p!=null&&q!=null)
{
this.Insert_order(q.data);
// System.out.println("t "+this+" t");
// System.out.println("m "+b+" m");
q=q.next;
}
this.ReverseLink1();
}
-
完整代碼如下:
private class Node{ private int data; private Node next; public Node() {} // 初始化全部屬性的構造器 public Node(int data, Node next) { this.data = data; this.next = next; } } public Node header;// 保存頭結點 private Node tail;// 保存尾節點 private int size;// 保存已含有的節點數 public void Input_size(int size){ this.size=size; } public My_list(){ header=null; tail=null; } public My_list(int e){ header=new Node(e,null); tail=header; size++; } public int length(){ return size; } // 獲取指定節點
private Node getNodeByIndex(int index){ if(index < 0 || index >= size) throw new IndexOutOfBoundsException("索引超出線性表範圍"); Node current = header;//從header開始遍歷 for(int i=0; i<size && current!=null; i++,current=current.next){ if(i == index) return current; } return null; } // 獲取指定索引處的元素 public int get(int index) { return this.getNodeByIndex(index).data; } public int locate(int e){ Node current=header; for(int i=0;i<size&¤t!=null;i++,current=current.next){ if(current.data==e) return i; } throw new IndexOutOfBoundsException("索引超出線性表範圍"); } //指定位置插入元素 public void Insert(int e,int index){ if(index<0||index>=size) throw new IndexOutOfBoundsException("索引超出線性表範圍"); // Node current =header; if(header == null)//空表直接尾部加 add(e); else{ if(index==0)//表頭直接在頭部加(已排除空表) addHead_e(e); else{ Node p=getNodeByIndex(index-1);//指定位置的前一個節點 p.next=new Node(e,p.next); } } size++; } ```//直接在尾部插入元素 public void add(int e){ if(header==null) { header = new Node(e, null); tail = header; } else{ Node p=new Node(e,null); tail.next=p; tail=p; } size++; } //在表頭插入元素 public void addHead_e(int e){ Node p=new Node(e,null); p.next=header; header=p; if(header.next==null) tail=header; size ++; } public void Insert_order(int e){ Node current = header; if(header == null) {//空表直接尾部加 add(e); } int i; for(i=0;i<size&¤t!=null;i++,current=current.next){ if(current.data>=e){//如果某個結點的指大於等於輸入值 if(i==0) {//如果是在頭節點,直接加在前面 addHead_e(e); break; } Node p=this.getNodeByIndex(i-1);//指定位置的前一個節點 p.next=new Node(e,p.next);//這是不在首位的情況,獲取當前節點的前一個節點並將e插在它後面,同時新節點的指向是原節點的下一個節點 size++; break; } } if(i==size)//如果循環沒有被break結束,那麼i的值與size的值相等, add(e);//這說明這個元素比原鏈表中任何一個都大,直接在尾部添加節點即可 //System.out.println(size); }
/* 1.先將當前節點的下一節點記錄下來, 2.然後將當前節點指向上一節點 3.再將當前節點記錄下來,讓記錄的下一節點變成當前節點 * */ public Node ReverseLink1(){ if(header==null||header.next==null)//如果表頭爲空或者當前鏈表只有一個節點,那麼直接返回就可以了 return header; Node p,q; p=null;//p用來保存上一節點,開始時,表頭沒有上一節點(表頭的上一節點爲空) q=header;//q用來保存當前節點,從表頭開始操作 while(q!=null){//只要當前節點不爲空,就要進行操作 Node r=q.next;////1.r保存當前節點的下一節點 q.next=p;//2.讓當前節點指向上一節點,第一次爲空,第二次開始指向上一節點 p=q;//3.讓上一節點變成當前節點(把當前節點記錄下來,記錄進p裏) q=r;//3.讓記錄的下一節點變成當前節點 } header=p; return header; } ```///* 兩個遞增鏈表合成一個遞減鏈表 利用前面的函數和輔助節點p,將b鏈表的值按大小順序插入到a表中, 形成包含a和b所有元素的遞增鏈表,再將鏈表用寫好的逆置方法逆置就可以了 //* */ public void Combine_order_increase(My_list b) { Node p,q; p=this.header; q=b.header; while(p!=null&&q!=null) { this.Insert_order(q.data); // System.out.println("t "+this+" t"); // System.out.println("m "+b+" m"); q=q.next; } this.ReverseLink1(); } @Override//重寫 public String toString() {//作輸出用 if(header==null){ return null; } else{ StringBuffer sb=new StringBuffer(""); for(Node current = header; current != null; current = current.next) sb.append(current.data).append(" "); int len = sb.length(); return sb.toString(); } } }
主程序入口:
public class Test {
public static void main(String[] args) throws IOException {
My_list a = new My_list();
My_list b = new My_list();
int t=1;
int size_a;
int size_b;
while (t!=0){
System.out.println("Please choose the function");
System.out.println("1.initialize the “a” linklist and the “b” linklist");
System.out.println("2.insert one data into “a” list");
System.out.println("3.reverse the a list ");
System.out.println("4.Combine the a and b list");
System.out.println("5.Output the a and b list");
t=KeyInput.readInt();
switch (t){
case 1:
System.out.println("Please input the size of a list");
size_a=KeyInput.readInt();
// a.Input_size(size_a);
System.out.println("Please input the size of b list");
size_b=KeyInput.readInt();
// b.Input_size(size_b);
System.out.println("Please input the data of a list");
for(int i=0;i<size_a;i++) {
int e=KeyInput.readInt();
a.add(e);
}
System.out.println("Please input the data of b list");
for(int i=0;i<size_b;i++) {
int e = KeyInput.readInt();
b.add(e);
}
break;
case 2:
int e=KeyInput.readInt();
a.Insert_order(e);
break;
case 3:a.ReverseLink1();
break;
case 4:a.Combine_order_increase(b);
break;
case 5:
System.out.println(a);
System.out.println(b);
break;
case 0:
System.out.println("Welcome to Use Again!");
break;
default:
System.out.println("No such a funcation was found");
System.out.println("Press any Key to Continue ");
System.in.read( );
break;
}
}
}
}
代碼參考了其他人的想法,事實上是一個修改和添加功能使其適應題目,參考的鏈接:
鏈接:自己動手寫一個單鏈表.
鏈接:鏈表的逆置(迭代法與逆置法).