本文表述了線性表及其基本操作的代碼【Java實現】
參考書籍 :《數據結構 ——Java語言描述》/劉小晶 ,杜選主編
線性表需要的基本功能有:動態地增長或收縮;對線性表的任何數據元素進行訪問和查找;在線性表中的任何位置進行數據元素的插入和刪除操作;求線性表中指定數據元素的前驅和后繼等等。
首先描述線性表的抽象類型,我們使用Java接口interface:
Ilist.java:
package liner_list;
public interface IList
{
public void clear();
public boolean isEmpty();
public int length();
public Object get(int i) throws Exception;
public void insertAt(int i,Object x) throws Exception;
public void remove(int i) throws Exception;
public int indexOf(Object x);
public void display();
}
其次描述順序表,其特點有:在線性表中的邏輯上相鄰的數據元素,在物理存儲位置上也是相鄰的;存儲密度高,但需要預先分配”足夠應用“的存儲空間,這可能將會造成存儲空間的浪費;便於隨機存儲;不便於插入和刪除,因為在順序表中進行插入和刪除操作會引起大量數據元素的移位。我們用SqList類描述順序表:
SqList.java:
package liner_list;
// 規定方法中的參數i都為順序表元素的索引(下標)
public class SqList implements IList
{
public Object[] listItem; // 順序表存儲空間
public int curLen; // 線性表的當前長度
public SqList(int maxSize)
{
listItem = new Object[maxSize]; // 為順序表分配maxSize個存儲單元
curLen = 0; // 置當前長度為0
}
public void clear()
{
curLen = 0; // 置當前長度為0,即規定為清空順序表,但是內存中還有數據存在
}
public boolean isEmpty()
{
return curLen == 0;
}
public int length()
{
return curLen; // 返回當前長度
}
public Object get(int i) throws Exception // 得到下標為i的元素,同時判斷異常
{
if (i >= curLen || i < 0) // 索引越界,0<=index<=curLen
{
throw new Exception("Argument 'i' is out of range!");
}
return listItem[i];
}
public void insertAt(int i, Object x) throws Exception // 在下表為i的位置插入元素x,同時判斷異常
{
if (curLen == listItem.length) // 判斷表滿
{
throw new Exception("SqList is full!");
}
if (i > curLen || i < 0) // 索引越界,可以在curLen的位置進行插入
{
throw new Exception("Argument 'i' is out of range!");
}
for (int j = curLen; j > i; j--) // j從curLen的位置開始,即當前表最后一個元素的后一個位置,從而使得i位置及以后位置上的元素向后移一位
{
listItem[j] = listItem[j - 1];
}
listItem[i] = x; // 將x元素插入i位置
curLen++; // 插入后表長加一
}
public void remove(int i) throws Exception
{
if (i >= curLen || i < 0) // i小於0或者大於等於表長時拋出異常
{
throw new Exception("Argument 'i' is out of range!");
}
for (int j = i; j < curLen - 1; j++) // 從i位置開始向后,不能從最后開始,否則最后一個元素將覆蓋所有元素,若想從后向前,必須將被覆蓋的元素保留給下一個元素
{
listItem[j] = listItem[j + 1];
}
curLen--; // 刪除完后curLen減一
}
public int indexOf(Object x) // 規定返回-1表示未找到元素x
{
for (int i = 0; i < curLen; i++)
{
if (listItem[i].equals(x))
{
return i;
}
}
return -1;
// 書本代碼,效果相同
// int j = 0;
// while (j < curLen && !listItem[j].equals(x))
// {
// j++;
// }
// if (j < curLen)
// {
// return j;
// } else
// {
// return -1;
// }
}
public void display() // 輸出順序表中全部元素
{
System.out.println("****** SqList ******");
for (int i = 0; i < curLen; i++)
{
System.out.print(listItem[i] + " ");
}
System.out.println();
System.out.println("********************");
}
}
接着測試我們的順序表,使用SqListTest類來做測試:
SqListTest.java:
package liner_list;
import java.util.Scanner;
public class SqListTest
{
public static void main(String[] args) throws Exception
{
SqList sq1 = new SqList(10);
sq1.insertAt(0, "a0");
sq1.insertAt(1, "a1");
sq1.insertAt(2, "a2");
sq1.insertAt(3, "a3");
sq1.insertAt(4, "a4");
sq1.insertAt(5, "a5");
int index = sq1.indexOf("a2");
if (index != -1)
{
System.out.println("a2's index is " + index + "!");
} else
{
System.out.println("a5 is not in this SqList!");
}
sq1.display();
sq1.remove(2);
System.out.println("After remove:");
sq1.display();
SqList sq2 = new SqList(10);
Scanner sc = new Scanner(System.in);
System.out.println("Please input element:");
for (int i = 0; i < 8; i++)
{
sq2.insertAt(i, sc.next());
}
sc.close();
sq2.display();
}
}
運行我們的測試類,得到以下測試結果:

然后描述單鏈表,注意:我們推薦使用帶頭結點的單鏈表。這里總結以下關於頭指針和頭結點的問題:首先要清楚,head就是頭指針,毋庸置疑;如果有頭結點的話,head也頭結點,這里頭指針就是頭結點,一般說成頭指針指向頭結點,而head.next是下標為0的元素,規定 head是下標為-1的元素;如果沒有頭結點的話,head本是就是下標為0的元素,這里沒有頭結點,但是head還是頭指針。下面我們來描述結點類,它由兩部分組成,data數據域和next指針域:
Node.java:
package liner_list;
public class Node
{
public Object data; // 數據域
public Node next; // 指針域
public Node() // 無參構造方法
{
this(null, null);
}
public Node(Object data) // 帶一個參數的構造方法
{
this(data, null);
}
public Node(Object data, Node next) // 帶兩個參數的構造方法
{
this.data = data;
this.next = next;
}
}
我們用LinkList類來描述帶頭結點的單鏈表:
LinkList.java:
package liner_list;
import java.util.Scanner;
//關於頭結點與頭指針的問題
//首先要清楚,head就是頭指針,毋庸置疑
//如果有頭結點的話,head也頭結點,這里頭指針就是頭結點,一般說成頭指針指向頭結點,而head.next是下標為0的元素,規定 head是下標為-1的元素
//如果沒有頭結點的話,head本是就是下標為0的元素,這里沒有頭結點,但是head還是頭指針
//建議寫帶頭結點的單鏈表,此類就是一個典例
public class LinkList implements IList
{
public Node head;
public LinkList() // 無參構造方法,只構建頭指針
{
head = new Node();
}
public LinkList(int len, boolean Order) throws Exception // 帶有兩個參數的構造方法,分別為表長和插入的方式,規定true表示尾插法,flase表示頭插法
{
this();
if (Order)
{
createAtEnd(len);
} else
{
createAtHead(len);
}
}
public void createAtHead(int n) throws Exception // 頭插法
{
Scanner sc = new Scanner(System.in);
System.out.println("Please input element:");
for (int i = 0; i < n; i++)
{
insertAt(0, sc.next());
}
// sc.close(); // 不要關閉輸入流
// display();
}
public void createAtEnd(int n) throws Exception // 尾插法
{
Scanner sc = new Scanner(System.in);
System.out.println("Please input element:");
for (int i = 0; i < n; i++)
{
insertAt(length(), sc.next());
}
// sc.close(); // 不要關閉輸入流
// display();
}
@Override
public void clear() // 置空鏈表
{
head.data = null;
head.next = null;
}
@Override
public boolean isEmpty() // 鏈表判空
{
return head.next == null;
}
@Override
public int length() // 返回鏈表長度
{
Node p = head.next; // p指向首結點
int length = 0;
while (p != null)
{
p = p.next;
length++;
}
return length;
}
@Override
public Object get(int i) throws Exception
{
Node p = head.next;
int j = 0;
while (p != null && j < i) // 從首結點開始向后查找,直到p指向第i個結點或者p為空
{
p = p.next; // 指針后移
j++; // 計數加1
}
if (i < 0 || p == null) // i小於0或者大於表長減1時拋出異常
{
throw new Exception("Argument 'i' is out of range!");
}
return p.data;
}
@Override
public int indexOf(Object x) // 規定-1表示不在LinkList當中
{
Node p = head.next;
int index = 0;
while (p != null && !p.data.equals(x))
{
p = p.next;
index++;
}
if (p != null)
{
return index;
} else
{
return -1;
}
}
@Override
public void insertAt(int i, Object x) throws Exception
{
Node p = head; // 插入時從頭結點開始,因為可以插入在下標0的位置,也可以插入在下標為表長位置
int j = -1;
while (p != null && j < i - 1) // 找出下標為i-1的結點,即i結點的前驅
{
p = p.next;
j++;
}
if (i < 0 || p == null) // i小於0或者i大於表長時拋出異常
{
throw new Exception("Argument 'i' is out of range!");
}
Node s = new Node(x);
s.next = p.next;
p.next = s;
}
@Override
public void remove(int i) throws Exception
{
Node p = head;
int j = -1;
while (p != null && j < i - 1) // 找到下標為i-1的結點,即i結點的前驅
{
p = p.next;
j++;
}
if (i < 0 || p.next == null) // 拋出條件為i小於0或者i大於表長-1,所以此處為p.next==null
{
throw new Exception("Argument 'i' is out of range!");
}
p.next = p.next.next;
}
@Override
public void display()
{
Node p = head.next;
System.out.println("****** LinkList ******");
while (p != null)
{
System.out.print(p.data.toString() + " ");
p = p.next;
}
System.out.println();
System.out.println("*********************");
}
}
最后測試我們的單鏈表,使用LinkListTest類來做測試:
LinkListTest.java
package liner_list;
public class LinkListTest
{
public static void main(String[] args) throws Exception
{
LinkList linkList = new LinkList(10, true);
linkList.remove(0);
linkList.remove(1);
linkList.remove(linkList.length() - 1);
System.out.println("After remove:");
linkList.display();
linkList.insertAt(linkList.length(), "a9");
System.out.println("After insert:");
linkList.display();
int index = linkList.indexOf("a2");
if (index != -1)
{
System.out.println("a2's index is " + index + "!");
} else
{
System.out.println("a2 is not in this LinkList!");
}
}
}
運行我們的測試類,得到以下結果:

以上便是線性表中順序表和單鏈表最基礎的代碼描述,算法思想本文中沒有寫到,大家可以去看看前面說的那本參考書籍。此系列后面會陸續介紹更多有關數據結構的內容,也會更新一些關於數據結構的算法題目例子,謝謝大家支持!
