陣列、連結串列、Hash
在程式中,存放指定的資料最常用的資料結構有兩種:陣列和連結串列。
陣列和連結串列的區別:
1、陣列是將元素在記憶體中連續存放。
連結串列中的元素在記憶體中不是順序儲存的,而是通過存在元素中的指標聯絡到一起。
2、陣列必須事先定義固定的長度,不能適應資料動態地增減的情況。當資料增加時,可能超出原先定義的元素個數;當資料減少時,造成記憶體浪費。
連結串列動態地進行儲存分配,可以適應資料動態地增減的情況。
3、(靜態)陣列從棧中分配空間, 對於程式設計師方便快速,但是自由度小。
連結串列從堆中分配空間, 自由度大但是申請管理比較麻煩。
陣列和連結串列在儲存資料方面到底孰優孰劣呢?根據陣列和連結串列的特性,分兩類情況討論。
一、當進行資料查詢時,陣列可以直接通過下標迅速訪問陣列中的元素。而連結串列則需要從第一個元素開始一直找到需要的元素位置,顯然,陣列的查詢效率會比連結串列的高。
二、當進行增加或刪除元素時,在陣列中增加一個元素,需要移動大量元 素,在記憶體中空出一個元素的空間,然後將要增加的元素放在其中。同樣,如果想刪除一個元素,需要移動大量元素去填掉被移動的元素。而連結串列只需改動元素中的指標即可實現增加或刪除元素。
那麼,我們開始思考:有什麼方式既能夠具備陣列的快速查詢的優點又能融合連結串列方便快捷的增加刪除元素的優勢?HASH呼之欲出。
所謂的hash,簡單的說就是雜湊,即將輸入的資料通過hash函式得到一個key值,輸入的資料儲存到陣列中下標為key值的陣列單元中去。
我們發現,不相同的資料通過hash函式得到相同的key值。這時候,就產生了hash衝突。解決hash衝突的方式有兩種。一種是掛鏈式,也叫拉鍊法。掛鏈式的思想在產生衝突的hash地址指向一個連結串列,將具有相同的key值的資料存放到連結串列中。另一種是建立一個公共溢位區。將所有產生衝突的資料都存放到公共溢位區,也可以使問題解決。
如何實現hash的動態增加空間的效果?這和裝在因子密切相關。裝填因子 = 填入表中的元素個數 / 散列表的長度。當裝填因子達到一定值a時,我們就讓陣列增加一定的記憶體空間,同時rehash。
下面用兩個示例來加深理解。
示例一:用連結串列實現佇列
節點類
Java程式碼![收藏程式碼](http://1029975378-qq-com.iteye.com/images/icon_star.png)
- package cn.netjava.hash;
- public class LinkNode {
- //構造器:傳入Object物件
- public LinkNode(Object obj){
- data=obj;
- }
- public Object data; //Object物件
- public LinkNode next;//下一個節點
- //重寫toString方法
- public String toString(){
- //System.out.println(data);
- return (String)data;
- }
- //返回Object物件
- public Object getData(){
- return data;
- }
- //修改Onject物件
- public Object Update(Object o){
- data=o;
- return o;
- }
- }
佇列類
Java程式碼![收藏程式碼](http://1029975378-qq-com.iteye.com/images/icon_star.png)
- package cn.netjava.hash;
- public class LinkQueue {
- public LinkNode front=null;//第一個節點
- public LinkNode last=null;//最後一個節點
- public static void main(String args[]){
- LinkQueue lq=new LinkQueue();
- LinkNode lq1=new LinkNode("鄭睿1");
- LinkNode lq2=new LinkNode("鄭睿2");
- LinkNode lq3=new LinkNode("鄭睿3");
- LinkNode lq4=new LinkNode("鄭睿4");
- lq.InsertLinkNode(lq1);
- lq.InsertLinkNode(lq2);
- lq.InsertLinkNode(lq3);
- lq.InsertLinkNode(lq4);
- int count=lq.getLength();
- System.out.println("連結串列的長度為"+count);
- for(int i=0;i<count;i++){
- LinkNode ln = lq.getLinkNode(i);
- System.out.println("連結串列的第"+i+"個元素的的值為"+ln.getData().toString());
- }
- lq.deleteLinkNode(2);
- count=lq.getLength();
- System.out.println("連結串列現在的長度是"+lq.getLength());
- for(int i=0;i<count;i++){
- LinkNode ln = lq.getLinkNode(i);
- System.out.println("連結串列的第"+i+"個元素的的值為"+ln.getData().toString());
- }
- lq.getLinkNode(1).Update("更新後的物件鄭睿");
- for(int i=0;i<count;i++){
- LinkNode ln = lq.getLinkNode(i);
- System.out.println("連結串列的第"+i+"個元素的的值為"+ln.getData().toString());
- }
- for(int i=0;i<200;i++){
- LinkNode ln = new LinkNode(i);
- lq.InsertLinkNode(ln);
- }
- System.out.println("陣列長度為"+lq.getLength());
- }
- /**
- * 插入節點
- * @param obj:插入節點的物件
- */
- public void InsertLinkNode(Object obj){
- //當連結串列為空,新建一個節點並設定為第一個節點
- if(front==null){
- front=new LinkNode(obj);
- last=front;
- }
- //當連結串列不為空,新建一個節點並插入到最後一個節點的後面
- else{
- LinkNode next=new LinkNode(obj);
- last.next=next;
- last=next;
- }
- }
- /**
- *在指定索引下插入節點
- * @param index
- */
- public void insertIndexObj(int index,Object obj){
- //判斷輸入的索引是否越界,如果越界,則丟擲異常
- int total=getLength();
- if(index>total||index<0)
- throw new java.lang.RuntimeException("輸入的索引越界了!");
- LinkNode lNode=getLinkNode(index);
- LinkNode linkNode=new LinkNode(obj);
- lNode.insert(linkNode);
- }
- /**
- * 根據索引刪除連結串列
- * @param index:索引
- */
- public void deleteLinkNode(int index){
- //判斷輸入的索引是否越界,如果越界,則丟擲異常
- int total=getLength();
- if(index>total||index<0)
- throw new java.lang.RuntimeException("輸入的索引越界了!");
- if(front!=null){
- LinkNode n=front;
- LinkNode m=front;
- int count=0;
- while(n!=null){
- if(count==index){
- if(n.equals(front)){
- front=front.next;
- }
- else{
- m.next=n.next;
- }
- }
- m=n;
- n=n.next;
- count++;
- }
- }
- }
- /**
- * 根據索引取出節點
- * @param lNode:節點
- * @return:根據索引返回的節點
- */
- public LinkNode getLinkNode(int index){
- if(front==null)
- return null;
- LinkNode l=front;
- int count=0;
- while(l!=null){
- if(count==index)
- return l;
- count++;
- l=l.next;
- }
- return null;
- }
- /**
- * 得到連結串列的長度
- * @return:連結串列的長度
- */
- public int getLength(){
- if(front==null)
- return 0;
- LinkNode l=front;
- int count=0;
- while(l!=null){
- count++;
- l=l.next;
- }
- return count;
- }
- /**
- * 修改物件節點
- * @param index:物件節點索引
- * @param obj:修改物件內容
- */
- <