<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在之前對順序表使用C語言進行了簡單的實現:C語言線性表順序表示及實現
當時使用C語言進行實現是因為學校的教材使用的是C語言來進行實現,在後來的學習中,Java
成為了我的主語言,使用不同的語言對順序表來進行實現也可以加深我對語言的理解和應用。
順序表是在計算機記憶體中以陣列的形式儲存的線性表,線性表的順序儲存是指用一組地址連續的儲存單元依次儲存線性表中的各個元素、使得線性表中在邏輯結構上相鄰的資料元素儲存在相鄰的物理儲存單元中,即通過資料元素物理儲存的相鄰關係來反映資料元素之間邏輯上的相鄰關係,採用順序儲存結構的線性表通常稱為順序表。順序表是將表中的結點依次存放在計算機記憶體中一組地址連續的儲存單元中。
在順序表的定義中可以看到,順序表是一組地址連續的儲存單元,本質上是一種增加了一些基本操作功能的陣列
在本文中要實現的功能有:
實現工具 | 版本 |
---|---|
IntelliJ IDEA | 2021.3 |
JDK | 1.8 |
在 IntelliJ IDEA 中新建普通的Java
專案即可
在新建好Java工程後,我們建立自己的順序表類,在這裡我對當前類命名為Array
,在這裡實現泛型,同時Array
類中需要有兩個成員屬性:
data
,型別為泛型陣列size
,型別為int
兩個成員屬性的存取許可權都應該為private
,使用者不能夠直接進行修改,只能通過對應的getter
方法進行獲取。 在成員屬性中我們將存放資料的陣列和順序表中的元素數量只是進行了宣告,但是並未進行初始化,因此==初始化的過程就需要在構造方法中進行==
int
型別的資料capacity
,代表順序表的初始容量,因此對data
進行初始化泛型陣列即可。同時當前順序表中是沒有元素的,代表順序表中的元素個數size
的初始值為0。this(10)
進行有參構造的呼叫即可。注意: 在Java中不能直接初始化泛型陣列,需要先宣告Object
型別的陣列後通過強制型別轉換的方式將Object
型別的陣列轉換為泛型陣列
package net.csdn.array; /** * @author zhangrongkang * @date 2022/6/26 */ public class Array<E> { /** * 存放資料的陣列 */ private E[] data; /** * 陣列中元素的數量 */ private int size; /** * 建構函式,傳入陣列的容量capacity構造陣列 * * @param capacity 初始陣列大小 */ public Array(int capacity) { data = (E[]) new Object[capacity]; size = 0; } /** * 無參建構函式,預設陣列大小為0 */ public Array() { this(10); } }
使用泛型的原因:使用泛型後可以將當前順序表中儲存物件,如果不使用泛型的話只能使用自己指定型別的資料,擴充套件性不強。因此使用泛型後可以將當前順序表的使用擴充套件到所有類物件,只需要在建立時指定相應的物件即可。
/** * 獲取陣列中的元素個數 * * @return 陣列當前的元素個數 */ public int getSize() { return size; }
對於獲取當前順序表中的元素個數來說,因為我們定義的初始成員變數size
代表的含義就是當前順序表的元素個數,但是size
變數的本質為當前順序表的指標,指向順序表最後一個元素的下一個位置 (元素的索引從0開始,最後一個元素的索引值比元素個數值小 1),不能直接進行修改,因此要想獲取需要通過size
元素的getter
方法
同樣地,對於獲取順序表的元素個數只需要將size
返回即可
/** * 獲取陣列當前容量 * * @return 陣列當前容量 */ public int getCapacity() { return data.length; }
在對順序表進行宣告的時候,就已經將使用者傳來的或者預設的初始容量capacity
作為陣列的大小對data
泛型陣列進行了初始化,因此當前data
的length
屬性就是傳來的capacity
,(或者在後面進行動態擴容或縮容時,data.length
是一直不會改變的,改變的只有size
) 因此獲取順序表當前的容量將data.length
返回即可
/** * 判斷陣列是否為空 * * @return 陣列是否為空 */ public boolean isEmpty() { return size == 0; }
我們知道size
代表的是順序表中的元素個數,因此要判斷當前順序表是否為空僅需要將size
是否等於0進行返回即可
/** * 向陣列中索引為index位置新增元素e * * @param index 索引位置 * @param e 元素e */ public void add(int index, E e) { // 判斷陣列空間是否已滿 if (size == data.length) { // 對陣列進行擴容 resize(2 * data.length); } // 越界判斷 if (index < 0 || index > size) { throw new ArrayIndexOutOfBoundsException(); } for (int i = size - 1; i >= index; i--) { data[i + 1] = data[i]; } data[index] = e; size++; }
當向順序表中指定索引位置新增元素時要考慮以下幾個問題:
對於第一個問題來說,當順序表已滿沒有容量時,再進行新增元素時需要進行動態的擴容,resize
方法的作用就是對陣列進行動態的擴容以及縮容,對於resize
方法的實現我們放到後面進行具體的講解,在這裡我們知道如果當前順序表容量已滿,將順序表容量擴大為當前順序表容量的二倍
第二個問題的出現情況只有兩種,索引小於0或索引超過了當前順序表中的元素個數時,丟擲執行時異常即可
在索引沒有問題後,新增元素的過程如下圖所示:
要先將要新增的索引位置後的所有元素依次向後移動一位,在移動完成後將當前索引位置的元素使用要進行新增的元素對當前位置的元素進行覆蓋即可,同時新增完元素後將size++
,維護指標變數
/** * 在陣列末尾新增一個元素 * * @param e 要新增的元素 */ public void addLast(E e) { add(size, e); }
在末尾新增元素可以對之前寫好的向指定索引位置新增元素的程式碼進行復用,同時在add
方法中進行了校驗,因此對於擴容以及索引都問題都無需我們進行考慮,將 索引位置的引數賦值為當前最後一個元素的下一個位置size
後直接呼叫即可。
/** * 在陣列的頭部新增元素e * * @param e 要新增的元素 */ public void addFirst(E e) { add(0, e); }
在順序表的頭部新增元素也是同樣的道理,對指定索引位置插入元素進行復用即可,在此不進行贅述。
/** * 獲取索引為index位置的元素 * * @param index 索引位置 * @return 索引為index位置的元素 */ public E get(int index) { if (index < 0 || index >= size) { throw new ArrayIndexOutOfBoundsException(); } return data[index]; }
獲取指定索引位置的元素與之前在指定索引位置插入元素的思路大體一致,但是要更簡單一些,無需考慮順序表擴容以及縮容的問題,僅需要考慮傳入的索引值是否合法,如果傳入的索引值合法則直接將對應位置的元素進行返回即可。
/** * 獲取陣列中第一個元素 * * @return 陣列中第一個元素 */ public E getFirst() { return get(0); }
在實現了獲取指定索引位置的元素後,獲取順序表的第一個元素同樣是對get
方法的複用,將0做為索引值進行引數傳遞即可。
/** * 獲取陣列中最後一個元素 * * @return 陣列中最後一個元素 */ public E getLast() { return get(size - 1); }
獲取順序表最後一個元素也是對get
方法的複用,在此不進行贅述
/** * 設定索引為index位置的元素值為e * * @param index 索引位置 * @param e 要進行替換的元素值 */ public void set(int index, E e) { if (index < 0 || index >= size) { throw new ArrayIndexOutOfBoundsException(); } data[index] = e; }
在之前獲取指定索引位置的元素時,先判斷索引是否合法,如果合法將對應位置的元素進行返回。同理,先判斷索引位置是否合法,如果合法就將當前位置的元素使用我們接收到的元素e
進行替換。
/** * 判斷陣列中是否存在元素e * * @param e 元素e * @return 是否存在元素e */ public boolean contains(E e) { for (int i = 0; i < size; i++) { if (data[i].equals(e)) { return true; } } return false; }
對於判斷順序表中是否存在指定元素來說,對順序表進行線性查詢,如果找到了相應的資料,就返回true
,如果在對順序表遍歷結束後仍然沒有找到指定元素,說明當前順序表中不存在指定元素,返回false
注意:在這裡因為是物件的比較,使用equals
方法進行比較,如果是基本資料型別(如int
,double
等)的比較就要使用==
來進行比較
/** * 查詢陣列中元素e的索引,如果不存在返回 -1 * * @param e 元素e * @return 元素e在陣列中的索引 */ public int find(E e) { for (int i = 0; i < size; i++) { if (data[i].equals(e)) { return i; } } return -1; }
獲取指定元素的索引同樣使用線性查詢法,使用equals
進行比較,如果找到相同的元素則返回對應的索引值,如果遍歷完順序表後仍然沒有找到指定元素則返回-1,說明當前元素不存在。
/** * 刪除索引位置為 index 的元素並返回被刪除的元素 * * @param index 刪除元素的索引 * @return 被刪除的元素 */ public E remove(int index) { if (index < 0 || index >= size) { throw new ArrayIndexOutOfBoundsException(); } // 先將返回值進行儲存 E res = data[index]; for (int i = index + 1; i < size; i++) { data[i - 1] = data[i]; } size--; data[size] = null; // 如果當前陣列中的元素不足陣列容量的一半 if (size < data.length / 2) { // 重新分配空間 resize(data.length / 2); } return res; }
在之前進行元素的新增時要考慮順序表是否還有容量,在刪除元素時不需要考慮是否還有容量,但是也要考慮相應的有關於陣列縮容問題。因此要考慮以下問題:
對於第一個問題的解決方法為在刪除元素後,對當前順序表的元素個數與陣列的容量的一半進行比較,如果發現當前元素個數小於陣列容量的一半時,我們可以繼續呼叫resize
方法重新分配陣列的容量(resize
方法在之後會詳細解釋),當前的實現結果就是將陣列的容量縮容至原陣列都一半,對於記憶體的節省來說更有好處。
第二個問題解決方式與之前處理一樣,檢視索引值是否小於0以及是否大於等於當前順序表都元素個數
刪除元素的本質也是將當前索引值的後一個元素開始,依次向前移動,覆蓋掉前一個元素,最後再將size--
,維護指標,刪除結束後將臨時儲存的被刪除的元素返回即可。
刪除元素過程如下圖所示:
注意:順序表的刪除本質上是用後一個元素將前一個元素依次覆蓋,移動了size
指標後此時指標指向的元素仍然為原本順序表中的最後一個元素,因為在使用者的實際操作中,size
指向的元素無法被存取到,所以並沒有什麼影響。但是我們在這裡使用了泛型,在Java的GC
(垃圾回收機制)中因為此時順序表的最後一個元素仍然被size
指向參照,無法被回收,因此在這裡手動執行data[size] = null;
將當前的參照回收。
/** * 刪除並返回陣列的第一個元素 * * @return 陣列的第一個元素 */ public E removeFirst() { return remove(0); }
與之前的思路一致,在有了刪除指定索引位置的元素方法後,刪除並返回順序表第一個元素也是對剛才實現的remove
方法進行復用,在此不做贅述。
/** * 刪除並返回陣列中的最後一個元素 * * @return 陣列中的最後一個元素 */ public E removeLast() { return remove(size - 1); }
刪除順序表中最後一個元素同樣是對remove
方法的複用,在此也不多做贅述。
/** * 從陣列中刪除元素e * * @param e 陣列中被刪除的元素 * @return 是否刪除成功 */ public boolean removeElement(E e) { int index = find(e); if (index == -1) { return false; } remove(index); return true; }
刪除順序表中指定的元素本質上是對之前實現的獲取順序表中指定元素的索引方法和刪除指定索引位置元素方法的複用,首先通過find
方法獲取到要刪除元素的索引,接著對索引進行判斷,檢視當前元素是否存在,如果當前元素存在則將獲取到的索引值作為remove
方法的引數傳遞,將當前索引位置的元素刪除即可。
/** * 對陣列進行擴容 * * @param newCapacity 擴容後陣列的容量 */ private void resize(int newCapacity) { E[] newData = (E[]) new Object[newCapacity]; for (int i = 0; i < size; i++) { newData[i] = data[i]; } data = newData; }
在之前向順序表中新增元素以及刪除順序表中的元素都涉及到了擴容以及縮容的過程,其實對於擴容以及縮容來說區別只體現在了傳遞來的引數與原陣列容量大小的差別,擴容與縮容的思路都是宣告一個新的陣列,初始容量的大小為傳遞來的引數,接著遍歷原來的陣列,將每一個元素依次填充到新的陣列中,之後再將data
物件的參照指向新的陣列newData
即可。
在進行結果測試前,為了方便於觀察,在這裡我重寫了Array
類的toString
方法
@Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(String.format("Array: size = %d, capacity = %dn", size, data.length)); builder.append("["); for (int i = 0; i < size; i++) { builder.append(data[i]); if (i != size - 1) { builder.append(", "); } } builder.append("]"); return builder.toString(); }
以上便是Java
語言對線性表的順序表示和實現,和以前使用C語言來寫順序表最大的感受就是曾經覺得很難寫、很費腦的程式碼再次實現時感覺變得很容易了,同時對於很多的方法也有了複用的思想,對線性表的理解更加深刻。高興於自己成長的同時也更加深刻意識到以後的路會更加的艱難,希望自己可以在未來的道路上戒驕戒躁、穩紮穩打,哪怕再困難,也不會放棄!
以下是原始碼
package net.csdn.array; /** * @author zhangrongkang * @date 2022/6/26 */ public class Array<E> { private E[] data; /** * 陣列中元素的數量 */ private int size; /** * 建構函式,傳入陣列的容量capacity構造陣列 * * @param capacity 初始陣列大小 */ public Array(int capacity) { data = (E[]) new Object[capacity]; size = 0; } /** * 無參建構函式,預設陣列大小為0 */ public Array() { this(10); } /** * 獲取陣列中的元素個數 * * @return 陣列當前的元素個數 */ public int getSize() { return size; } /** * 獲取陣列當前容量 * * @return 陣列當前容量 */ public int getCapacity() { return data.length; } /** * 判斷陣列是否為空 * * @return 陣列是否為空 */ public boolean isEmpty() { return size == 0; } /** * 在陣列末尾新增一個元素 * * @param e 要新增的元素 */ public void addLast(E e) { add(size, e); } /** * 在陣列的頭部新增元素e * * @param e 要新增的元素 */ public void addFirst(E e) { add(0, e); } /** * 向陣列中索引為index位置新增元素e * * @param index 索引位置 * @param e 元素e */ public void add(int index, E e) { // 判斷陣列空間是否已滿 if (size == data.length) { // 對陣列進行擴容 resize(2 * data.length); } // 越界判斷 if (index < 0 || index > size) { throw new ArrayIndexOutOfBoundsException(); } for (int i = size - 1; i >= index; i--) { data[i + 1] = data[i]; } data[index] = e; size++; } /** * 獲取索引為index位置的元素 * * @param index 索引位置 * @return 索引為index位置的元素 */ public E get(int index) { if (index < 0 || index >= size) { throw new ArrayIndexOutOfBoundsException(); } return data[index]; } /** * 獲取陣列中第一個元素 * * @return 陣列中第一個元素 */ public E getFirst() { return get(0); } /** * 獲取陣列中最後一個元素 * * @return 陣列中最後一個元素 */ public E getLast() { return get(size - 1); } /** * 設定索引為index位置的元素值為e * * @param index 索引位置 * @param e 要進行替換的元素值 */ public void set(int index, E e) { if (index < 0 || index >= size) { throw new ArrayIndexOutOfBoundsException(); } data[index] = e; } /** * 判斷陣列中是否存在元素e * * @param e 元素e * @return 是否存在元素e */ public boolean contains(E e) { for (int i = 0; i < size; i++) { if (data[i].equals(e)) { return true; } } return false; } /** * 查詢陣列中元素e的索引,如果不存在返回 -1 * * @param e 元素e * @return 元素e在陣列中的索引 */ public int find(E e) { for (int i = 0; i < size; i++) { if (data[i].equals(e)) { return i; } } return -1; } /** * 刪除索引位置為 index 的元素並返回被刪除的元素 * * @param index 刪除元素的索引 * @return 被刪除的元素 */ public E remove(int index) { if (index < 0 || index >= size) { throw new ArrayIndexOutOfBoundsException(); } // 先將返回值進行儲存 E res = data[index]; for (int i = index + 1; i < size; i++) { data[i - 1] = data[i]; } size--; data[size] = null; // 如果當前陣列中的元素不足陣列容量的一半 if (size < data.length / 2) { // 重新分配空間 resize(data.length / 2); } return res; } /** * 刪除並返回陣列的第一個元素 * * @return 陣列的第一個元素 */ public E removeFirst() { return remove(0); } /** * 刪除並返回陣列中的最後一個元素 * * @return 陣列中的最後一個元素 */ public E removeLast() { return remove(size - 1); } /** * 從陣列中刪除元素e * * @param e 陣列中被刪除的元素 * @return 是否刪除成功 */ public boolean removeElement(E e) { int index = find(e); if (index == -1) { return false; } remove(index); return true; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(String.format("Array: size = %d, capacity = %dn", size, data.length)); builder.append("["); for (int i = 0; i < size; i++) { builder.append(data[i]); if (i != size - 1) { builder.append(", "); } } builder.append("]"); return builder.toString(); } /** * 對陣列進行擴容 * * @param newCapacity 擴容後陣列的容量 */ private void resize(int newCapacity) { E[] newData = (E[]) new Object[newCapacity]; for (int i = 0; i < size; i++) { newData[i] = data[i]; } data = newData; } }
package net.csdn.array; /** * @author zhangrongkang * @date 2022/6/26 */ public class ArrayMain { public static void main(String[] args) { System.out.println("宣告新的順序表,初始容量為10:"); Array<Integer> array = new Array<>(10); for (int i = 0; i < 10; i++) { array.addLast(i); } System.out.println(array + "n"); System.out.println("向索引為 1 的位置新增元素 100:"); array.add(1, 100); System.out.println(array + "n"); System.out.println("在順序表的頭部新增 -1:"); array.addFirst(-1); System.out.println(array + "n"); System.out.println("在順序表的尾部新增 101:"); array.addLast(101); System.out.println(array + "n"); System.out.println("移除索引位置為 2 的元素:"); array.remove(2); System.out.println(array + "n"); System.out.println("移除順序表中的元素 4:"); array.removeElement(4); System.out.println(array + "n"); System.out.println("移除順序表中的第一個元素:"); array.removeFirst(); System.out.println(array + "n"); System.out.println("移除順序表中的最後一個元素:"); array.removeLast(); System.out.println(array + "n"); System.out.println("元素7的索引位置為:" + array.find(7)); System.out.println("陣列中是否包含元素4:" + array.contains(4)); } }
到此這篇關於Java線性表的順序表示及實現的文章就介紹到這了,更多相關Java線性表內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45