【線性表基礎】順序表和單鏈表的插入、刪除等基本操作【Java版】


本文表述了線性表及其基本操作的代碼【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!");
		}
	}
}

運行我們的測試類,得到以下結果:

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM