嵌入式linux c 學習筆記9---雜湊連結串列
/*
* =====================================================================================
*
* Filename: hash.c
*
* Description: hash表
* Author: [email protected]
* =====================================================================================
*/
hash table 又稱散列表,是一種線性儲存結構。
hash的基本思想是:設要儲存的物件個數為n,設定一個長度為m(m>=n)的連續記憶體單元,以線性表中的每個物件的關鍵字ki(0=<i<n-1)為自變數,通過一個雜湊函式的函式h(ki),把ki對映為記憶體單元的地址,並把該物件儲存在這個記憶體單元中。h(ki)也稱為雜湊地址。
雜湊衝突:對於兩個關鍵字ki和kj(i!=j),有ki!=kj,但是有h(ki)=h(kj).
同常把具有不同關鍵字而具有相同雜湊地址的物件稱為“同義詞”,由同義詞引起的衝突稱為同義詞衝突。
雜湊函式的構造方法:
1: 直接地址法:
直接地址法是以關鍵字k本生或關鍵字加上某個數值常量c作為雜湊地址的方法。
h(k)=k+c
這種方法簡單,當關鍵字的分佈基本連續時,可用直接地址法的雜湊函式,否則,若關鍵字分佈不連續將造成記憶體單元的大量浪費。
2:除留餘數法:
用關鍵字k除以某個不大於雜湊表長度m的數p所得的餘數作為雜湊地址的方法
h(k)=k%p
p應該取不大於m的素數效果最好。
3:數字分析法:
提取關鍵字中取值教均勻的數字位作為雜湊地址的方法。
雜湊衝突解決的辦法:
基本思想是當發生雜湊衝突時通過雜湊衝突函式產生一個新的雜湊地址使hl(ki)!=
hl(kj),雜湊衝突函式產生的雜湊地址仍可能有雜湊衝突問題,此時,在用一個新
的雜湊衝突函式得到新的雜湊地址,一直到不存在雜湊衝突為止。
1:開放地址法:雜湊空閒單元既向同義詞關鍵字開發,也向發生衝突的非同義詞開放。
a:線性探查法:
從發生衝突的地址開始,依次探查d的下一個地址,直到找到一個空閒地址為止,公式描述為:
d0=h(k);
di=(di+1)modm (1<<i<<m-1)
b:平方探查法:
d0=h(k);
2
di=(d0+i )mod m (1<<i<<m-1)
缺點:不能探查到雜湊表上的所有單元,但是至少能探查到一半的單元。
2:拉鍊法:
拉鍊法是把所有的同義詞用單鏈錶鏈接起來的方法,在這中方法中,雜湊表每個單元存放的不再是記錄本身,而是相應同義詞單鏈表的頭指標。
雜湊表的運算:
雜湊表的目的主要是用於快速查詢,且插入和刪除均要用到查詢操作。
以下是有關的型別說明:
#define maxsize 100 /*定義最大雜湊表的長度*/
#define NULLKEY -1 /*定義空關鍵值*/
#define DELKEY -2 /*定義被刪除關鍵字值*/
typedef int keytype;
typedef char * infotype;
typedef struct
{
keytype key;
infotype data;
int count;
}hashtable[maxsize];
1:雜湊表的查詢:
雜湊表的查詢和建表過程類似,假設給定的值是k,根據建表是設定的雜湊函式h,計算出雜湊地址h(k),若表中該地址單元不為空,且該地址的關鍵字不等於k,則按建表時設定的處理衝突的方法找下一個地址。
int searchht(hashtable ha,int p,keytype k)
{
int i=0;
int adr;
adr=k%p;/*我們這裡用的是除留餘數法來構建的hash表*/
while(ha[adr].key!=NULLKEY&&ha[adr].key!=k)
{
i++;
adr=(adr+1)%p;
}
if(ha[adr],key==k)
{
return adr;
}
else
return -1;
}
2:刪除
採用開放地址法處理衝突的雜湊表上執行刪除操作,只能在被刪除記錄上做刪除標記,而不能真正刪除。
int deleteht(hashtable ha,int p,int k,int &n)/*刪除雜湊表中的關鍵字k*/
{
int adr;
adr=searchht(ha,p,k);
if(adr!=-1)
{
ha[addr].key=DELKEY;
n--;/*雜湊表的長度減1*/
return 1;
}
else
return 0;
}
3:建表和插入
建表是首先要將表中的個各節點的關鍵字清空,使其地址為開放的,讓後呼叫插入演算法將給定的關鍵字序列依次插入表中,插入演算法首先呼叫查詢演算法,若在表中找到代插入的關鍵字,則插入失敗,若在表中找到一個開放的地址,則將代插入的節點插入其中。
void insertht(hashtable ha,int &n,keytype k,int p)
{
int i,adr;
adr=k%p;
if(ha[adr].key==NULLKEY||ha[adr].key==DELKEY)
{
ha[adr].key=k;
ha[adr].count=1;
}
else
{
i=1;
do
{
adr=(adr+1)%p;
i++;
}while(ha[adr].key!=NULLKEY&&ha[adr].key!=DELKEY);
ha[adr].key=k;
ha[adr].count=i;
}
n++;
}
void creatht(hashtable ha,keytype x[],int n,int m,int p)
{
int i,n1=0;
for(i=0;i<m;i++)
{
ha[i].key=NULLKEY;
ha[i].count=0;
}
for(i=0;i<n;i++)
{
insertht(ha,n1,x[i],p);/*呼叫插入演算法在ha中插入x[i].*/
}
}
問題描述:
根據本書提供的Linux核心list.h標頭檔案,實現一個帶雜湊連結串列功能的程式。其中宿主節點hlist_node僅包含一個數據成員char *name,要求能完成根據給定的字串,在該雜湊連結串列中進行宿主節點的查詢匹配、增加和刪除。
程式如下:
/*
* =====================================================================================
*
* Filename: hash_oper.c
*
* Description: 雜湊表的操作
*
* Version: 1.0
* Created: 2010年11月07日 15時39分29秒
* Revision: none
* Compiler: gcc
*
* Author: Yang Shao Kun (),
* Company: College of Information Engineering of CDUT
*
* =====================================================================================
*/
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include"list.h"
#define maxsize 15 /*定義最大雜湊表的長度 */
#define NULLKEY -1 /*定義空關鍵字的值 */
#define DELKEY -2 /*定義被刪除關鍵字的值 */
#define NULL ((void *)0)
struct hashtable {
char *name;
struct hlist_node node;
};
/*一個簡單的hash函式*/
unsigned int BKDhash(char *str)
{
unsigned int send = 131;
unsigned int hash = 0;
while (*str) {
hash = hash * send + (*str++);
}
return (hash & 0x7fffffff);
}
void my_hlist_entry(struct hlist_node *pos, struct hlist_head *head);
void print(int n, struct hashtable ha[]);
int main(int argc, char *argv[])
{
int i;
int adr, t;
struct hlist_head *p, *head;
struct hlist_node *pos;
struct hashtable my_hashtable[maxsize];
struct hashtable *temp;
struct hashtable *ptr1, *ptr;
char string[11] =
{ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', '/0' };
/*初始化 */
for (i = 0; i < maxsize; i++)
INIT_HLIST_NODE(&my_hashtable[i].node);
for (i = 0; i < maxsize; i++) {
my_hashtable[i].name = NULL;
}
head = (struct hlist_head *) malloc(sizeof(struct hlist_head));
INIT_HLIST_HEAD(head);
p = head;
for (i = 0; string[i] != '/0'; i++) {
adr = BKDhash(&string[i]);
adr = adr & maxsize;
if ((my_hashtable[adr].name) == NULL) {
if (i == 0 && my_hashtable[adr].name == NULL) {
my_hashtable[adr].name = &string[i];
hlist_add_head(&(my_hashtable[adr].node),
head);
pos = &(my_hashtable[adr].node);
} else {
(my_hashtable[adr].name) = &string[i];
hlist_add_after(pos,
&(my_hashtable[adr].node));
pos = pos->next;
}
} else {
do {
adr = (adr + 1) & maxsize;
} while (my_hashtable[adr].name != NULL);
(my_hashtable[adr].name) = &string[i];
hlist_add_after(pos, &(my_hashtable[adr].node));
pos = pos->next;
}
}
/*列印hash */
printf
("遍歷整個連結串列的順序,並輸出到螢幕,使用 hlist_for_each()/n");
my_hlist_entry(pos, head);
printf("/n");
/*節點的對應的陣列下標---雜湊值,和對應節點存放的字元 */
printf("元素對應的陣列下標為:/n");
print(maxsize, my_hashtable);
/*查詢和插入的實現,如是 字元 c,並在該宿主節點後加入新節點,元素為s */
printf
("我們查詢資料元素為字元‘c’的節點,並在節點後面加入資料元素為字元‘s’的節點/n");
char arr = 's';
hlist_for_each(pos, p) {
temp = hlist_entry(pos, struct hashtable, node);
if (temp->name == &string[2]) {
printf("find 'c'/n");
adr = BKDhash(&arr);
adr = adr & maxsize;
if ((my_hashtable[adr].name) == NULL
|| *(my_hashtable[adr].name) == DELKEY) {
my_hashtable[adr].name = &arr;
hlist_add_before(&my_hashtable[adr].node,
&temp->node);
} else {
do {
adr = (adr + 1) & maxsize;
} while (my_hashtable[adr].name != NULL
&& *(my_hashtable[adr].name) !=
DELKEY);
my_hashtable[adr].name = &arr;
hlist_add_before(&my_hashtable[adr].node,
&temp->node);
}
printf
("插入節點後,元素對應的下標為:/n");
print(maxsize, my_hashtable);
printf
("插入新節點後,連結串列順序變為:/n");
break;
}
}
my_hlist_entry(pos, head);
printf("/n");
/*節點的刪除,現在我們來刪除剛才新增的節點 */
/*首先呼叫查詢,查詢節點,s */
hlist_for_each(pos, p) {
temp = hlist_entry(pos, struct hashtable, node);
if (temp->name == &arr) {
printf("find s,then delete it/n");
hlist_del_init(pos);
/*我們b不free掉記憶體,只是做一個標記,表示已經刪除 */
*((*temp).name) = DELKEY;
}
}
print(maxsize, my_hashtable);
printf("刪除該節點後,連結串列遍歷/n");
my_hlist_entry(pos, head);
printf("/n");
return 0;
}
void print(int n, struct hashtable ha[maxsize])
{
int i;
for (i = 0; i < n; i++) {
if ((ha[i].name) != NULL && *(ha[i].name) != DELKEY) {
printf("---%d--->%c/n", i, *(ha[i].name));
}
}
}
void my_hlist_entry(struct hlist_node *pos, struct hlist_head *head)
{
struct hashtable *temp;
hlist_for_each(pos, head) {
temp = hlist_entry(pos, struct hashtable, node);
printf("->%c", *(temp->name));
}
}
相關推薦
嵌入式linux c 學習筆記9---雜湊連結串列
/* * ===================================================================================== * * Filename: hash.c * * Descri
43、我的C#學習筆記9
c#特殊運算符:is運算符is運算符用於檢查變量是否為指定的類型。如果是,返回真;否則,返回假。比如:創建一個控制臺應用程序,判斷整型變量i是否為整數類型。代碼如下:int i=0;bool result=(i is int);Console.WriteLine(result);Console.ReadLin
函式的過載(C++學習筆記 9)
C語言不允許過載 在傳統C語言中,函式名必須是唯一的,也就是說不允許出現同名的函式, 例如,當要求編寫求整數、長整型數和雙精度數的二次方的函式時,若用C來處理,必須編寫3個函式,這3個函式的函式名不允許同名。例如: Isquare( int i ); //求整數的二次方
Linux C學習筆記——txt檔案讀寫
/*************** perror(s) 用來將上一個函式發生錯誤的原因輸出到標準裝置(stderr)。引數 s 所指的字串會先打印出,後面再加上錯誤原因字串。此錯誤原因依照全域性變數errno的值來決定要輸出的字串。 FILE * fopen(const c
開發板的I/O在哪裡——韋東山嵌入式Linux視訊學習筆記01
一般來說,串列埠就是開發板的I/O(輸入輸出)介面。我們可以通過串列埠向板子傳送命令,也可以通過串列埠把板子的狀態資訊打印出來。 板載串列埠和PC的連線 如果板子上有串列埠,且電腦上也有串列埠,那直接用串列埠線相連就可以。對於沒有串列埠的膝上型電腦,可以
用OpenJTAG燒寫程式到Flash—— 韋東山嵌入式Linux視訊學習筆記03
說明:本文僅在Windows環境下實驗。 韋東山的JZ2440(v2),可以選擇從Nor Flash啟動,也可以選擇從Nand Flash啟動,不管從哪裡啟動,都需要Flash上有程式,沒有程式的話,板子就是一塊磚頭。 按照常規思維,板子上應該有一個Boot
【LINUX C學習筆記 3】管道通訊2-標準流管道
從檔案結構體指標stream中讀取資料,每次讀取一行。讀取的資料儲存在buf指向的字元陣列中,每次最多讀取bufsize-1個字元(第bufsize個字元賦'\0'),如果檔案中的該行,不足bufsize個字元,則讀完該行就結束。如若該行(包括最後一個換行符)的字元數超過bufsize-1,則fgets只返
C++ STL學習筆記四 list雙向連結串列容器
/* * ******************************************** * list雙向連結串列容器的基礎說明: ******************************************** * * list雙向連結串列容器採用雙向
hlist 雜湊連結串列
Linux 連結串列設計者(因為 list.h 沒有署名,所以很可能就是 Linus Torvalds)認為雙頭(next、prev)的雙鏈表對於 HASH 表來說 "過於浪費",因而另行設計了一套用於 HASH 表應用的 hlist 資料結構--單指標表頭雙迴圈連結串列,從
雜湊連結串列及其變種
前言 先來直觀的比較下普通連結串列和雜湊連結串列: 普通連結串列 普通連結串列的表頭和節點相同 struct list_head { struct list_head *next, *prev; }; 雜湊連結串列 雜湊連結串列頭 struct hl
(學習筆記 5)靜態連結串列
靜態連結串列(static linked list):用陣列來描述的連結串列,用陣列元素的下標來模擬單鏈表的指標。這種描述方法叫做遊標實現法。 遊標 5 2 3 4 5 6 7 … 1 資料 A
高質量嵌入式Linux C程式設計 第二章 資料 學習筆記
一、什麼是資料型別? 資料型別包含兩方面的內容,資料的表示和對資料加工的操作。資料的全部可能表示構成資料型別的值的集合,資料全部合理的操作構成資料型別的操作集合。 二、什麼是變數? 其值在作用域內可以改變的量稱為變數。一個變數應該有一個自己的名字,在記憶體中佔據
sqlite學習筆記9:C語言中使用sqlite之插入數據
name article void num mes cut cpp content int 前面創建了一張表,如今給他插入一些數據。插入數據跟創建表差點兒相同,不過SQL語言不一樣而已,完整代碼例如以下: #include <stdio.h> #inclu
Linux學習筆記9——bash的配置
bash站在用戶登錄的角度來說,SHELL的類型:登錄式shell:正常通常某終端登錄su - USERNAME (完全切換)su -l USERNAME 非登錄式shell:su USERNAME (半切換)圖形終端下打開命令窗口自動執行的shell腳本 由於你在用戶中設置了某些配置後,如果你再次登陸的
UNIX C 學習筆記一:UNIX/Linux發展歷史以及相關概念
一、UNIX 與 Linux 的發展歷史 Unix 作業系統是一個強大的多使用者,多工作業系統,支援多種處理器架構,按照作業系統的分類,屬於分時作業系統,最早由 Ken Thompson, Dennis Titchie 和 Douglas Mcllroy 於 1969年在 AT&
c學習筆記2018.10.9
這期筆記整理一下以前學的內容,emmmmm 不一定會 畢竟好長時間了,擺出來可以經常複習複習 Hello world 1 #include<stdio.h> #include <stdlib.h> int main() { puts(
高質量嵌入式Linux C程式設計 第三章 運算子、表示式學習
一、運算子有哪幾類? (1)算數運算子:+、-、*、/、%、++、–七種 (2)關係運算符:>、<、==、>=、<=、!=六種 (3)邏輯運算子:&&、||、!三種 (4)位操作運算子:&、|、~、^、<&l
高質量嵌入式Linux C程式設計 第四章 語句 學習
一、語句從流程的角度可以分為幾種 三種基本結構:順序結構、分支結構、迴圈結構 二、空語句有什麼作用 (1)純粹消耗CPU時間,起到延時作用 (2)為了程式的結構清楚,可讀性好,以後擴充新功能方便。 三、 表示式語句的構成 表示式語句由表示式加上;號構成 四、布
linux下c++學習筆記——c++編譯器安裝(CLion)
c++編譯器安裝 參考部落格 https://blog.csdn.net/weixin_36926794/article/details/80291034 啟用 https://blog.csdn.net/krais_wk/article/details/80970355 wget
linux下c++學習筆記——c++編譯執行
c++編譯執行 預處理 合併多個單張圖片的txt檔案為一個訓練txt cat *.txt > train.txt 替換檔案中的特定字串(將’80 '替換為’1 ') // find "file"|xargs perl -pi -e 's|old|new|g' fi