icoding复习7, 8
icoding復習7 哈希,AVL 查找
 必考點!!!?
 1. 哈希表創建
 typedef enum{
 ? ? HASH_OK,
 ? ? HASH_ERROR,
 ? ? HASH_ADDED,
 ? ? HASH_REPLACED_VALUE,
 ? ? HASH_ALREADY_ADDED,
 ? ? HASH_DELETED,
 ? ? HASH_NOT_FOUND,
 } HASH_RESULT;
 typedef struct __HashEntry HashEntry;
 struct __HashEntry{
 ? ? union{
 ? ? ? ? char ?*str_value;
 ? ? ? ? double dbl_value;
 ? ? ? ? int ? ?int_value;
 ? ? } key;
 ? ? union{
 ? ? ? ? char ?*str_value;
 ? ? ? ? double dbl_value;
 ? ? ? ? int ? ?int_value;
 ? ? ? ? long ? long_value;
 ? ? ? ? void ?*ptr_value;
 ? ? } value;
 ? ? HashEntry *next;
 };
 struct __HashTable{
 ? ? HashEntry **bucket; ? ? ? ?
 ? ? int size;
 ? ? HASH_RESULT last_error;
 };
 typedef struct __HashTable HashTable;
 // 創建大小為hash_size的哈希表,創建成功后返回HashTable類型的指針,否則返回NULL。
 HashTable *create_hash(int hash_size);
 哈希表相關說明:
HASH_RESULT 類型為相關函數的返回類型
 HashEntry 為哈希表所保存元素(即鍵值對 《key, value》)類型
 HashTable 為哈希表,其中 bucket 指向大小為size的、元素類型為 HashEntry*的指針數組
 哈希表采用鏈地址法處理沖突
 請實現 create_hash 函數,創建指定大小的哈希表。
#include 
 #include 
 #include "hash.h"
HashTable* create_hash(int size){
 ?? ?HashTable *table;
 ?? ?
 ?? ?if(!(table = (HashTable *)malloc(sizeof(HashTable)))) return false;
 ?? ?
 ?? ?if(!(table->bucket = (HashTable *)malloc(size * sizeof(HashEntry *)))){
 ?? ??? ?free(table); return false;
 ?? ?}
 ?? ?table->size = size;
 ?? ?table->last_error = HASH_OK;
 ?? ?return table;?
 }
必考!!!?
 2. 哈希表添加
 // 向哈希表中添加元素,其中鍵類型為char*, 元素類型為int。
 HASH_RESULT hash_add_int(HashTable * table, const char * key, int value);
哈希表相關說明:
 HASH_RESULT 類型為相關函數的返回類型
 HashEntry 為哈希表所保存元素(即鍵值對 《key, value》)類型
 HashTable 為哈希表,其中 bucket 指向大小為size的、元素類型為 HashEntry*的指針數組
 哈希表采用鏈地址法處理沖突
 請實現 hash_add_int 函數,向哈希表中添加元素,其中鍵類型為char*, 元素類型為int。
 在添加過程中,如果要添加的鍵值key已在哈希表中,且對應的值value也已存在,則函數返回 HASH_ALREADY_ADDED;
 如果要添加的鍵值key已在哈希表中,但對應的值value不同,函數將value值更新到哈希表中,之后返回 HASH_REPLACED_VALUE
 如果要添加的鍵值key不在哈希表中,則函數創建 HashEntry 類型,并將其加入到哈希表中,且函數返回 HASH_ADDED。
 本題所用的哈希函數如下:
 long hash_string(const char *str){
 ?? ?...
 }
HASH_RESULT hash_add_int(HashTable *table, const char *key, int value){
 ?? ?int i = hash_string(key) % table->size;
 ?? ?HashEntry *p;
 ?? ?
 ?? ?p = table->bucket[i];
 ?? ?
 ?? ?if(!p){//該關鍵字不存在?
 ?? ??? ?p = (HashEntry *)malloc(sizeof(HashEntry)); // 判空略
 ?? ??? ?strcpy(p->key.str_value, key);
 ?? ??? ?p->value.int_value = value;?
 ?? ??? ?table->bucket[i] = p;
 ?? ??? ?
 ?? ??? ?return HASH_ADDED;
 ?? ?}
 ?? ?//關鍵字存在,先判斷關鍵字是否相等,再判斷值;如果關鍵字不等那么最后還要鏈地址添加
 ?? ?while(p){
 ?? ??? ?if(strcmp(p->key.str_value, key)){
 ?? ??? ??? ?if(p->value.int_value == value){
 ?? ??? ??? ??? ?return HASH_ALREADY_ADDED;
 ?? ??? ??? ?}
 ?? ??? ??? ?else{
 ?? ??? ??? ??? ?p->value.int_value = value;
 ?? ??? ??? ??? ?return HASH_REPLACED_VALUE;
 ?? ??? ??? ?}
 ?? ??? ?}
 ?? ??? ?else
 ?? ??? ??? ?p = p->next;
 ?? ?}?
 ?? ?HashEntry *q = (HashEntry *)malloc(sizeof(HashEntry)); // 判空略
 ?? ?q->key = (char *)malloc(sizeof(char) *strlen(key));//判空類似前面的?
 ?? ?
 ?? ?strcpy(q->key.str_value, key);
 ?? ?q->value.int_value = value;
 ?? ?p->next = q;
 ?? ?q->next = NULL;
 ?? ?return HASH_ADDED;?
 }
3. AVL添加
 平衡二叉樹,是一種二叉排序樹,其中每個結點的左子樹和右子樹的高度差至多等于1。
 它是一種高度平衡的二叉排序樹?,F二叉平衡樹結點定義如下:
typedef struct node
 {
 ? ? int val;
 ? ? struct node *left;
 ? ? struct node *right;
 ? ? struct node *parent;
 ? ? int height;
 } node_t;
 //請實現平衡二叉樹的插入算法:
//向根為 root 的平衡二叉樹插入新元素 val,成功后返回新平衡二叉樹根結點
 node_t *avl_insert(node_t *root, int val);
#include 
 #include 
 #include "avl.h"
int get_height(node_t *p){
 ?? ?if(!p)
 ?? ??? ?return 0;
 ?? ?else
 ?? ??? ?return p->height;
 }
node_t* avl_insert(node_t *root, int val){
 //首先清晰字母定義;
 //val新插入結點元素值,height高度!!!?
 //定義查找過程中出現的距離插入結點最近的平衡因子不為零的結點A
 //定義A的孩子結點為B,需要旋轉的結點
 //定義插入節點為s,s的值為val
 //平衡因子:左子樹減去右子樹深度?
?? ?node_t *s, *A, *B, *C, *p, *fp;
 ?? ?//依次:插入點, 失衡點(也可能是旋轉點),旋轉點,旋轉點(也可能是插入點=s),動點,跟蹤點
 ?? ?int i, k;//平衡因子?
 ?? ?
 ?? ?s = (node_t *)malloc(sizeof(node_t));
 ?? ?if(!s) return NULL;
 ?? ?
 ?? ?s->val = val;
 ?? ?s->left = s->parent = s->right = NULL;
 ?? ?s->height = 1;
 ?? ?
 ?? ?//類似于指針跟蹤技術,p為動指針,A為跟蹤指針?
 ?? ?A = root; A->parent = NULL;
 ?? ?p = root; p->parent = NULL;
 ?? ?
 ?? ?//找出A?
 ?? ?if(!p)
 ?? ??? ?root = s;
 ?? ?else{
 ?? ??? ?while(p){
 ?? ??? ??? ?//先找出最近的A->height!=0的結點, 就是最后的失衡點
 ?? ??? ??? ?i = get_height(p->left) - get_height(p->right);?
 ?? ??? ??? ?if(i){
 ?? ??? ??? ??? ?A = p;
 ?? ??? ??? ??? ?A->parent = p->parent;
 ?? ??? ??? ?}
 ?? ??? ??? ?//fp跟蹤p,因為下一步p會往下移動,p最終指向s的那一層?
 ?? ??? ??? ?fp = p;
 ?? ??? ??? ?if(val < p->val)
 ?? ??? ??? ??? ?p = p->left;
 ?? ??? ??? ?else
 ?? ??? ??? ??? ?p = p->right;
 ?? ??? ??? ?}//p最終指向NULL就退出循環?? ??
 ?? ?}?
 ?? ?
 ?? ?//插入, 此時fp是p的前一個結點,p指向空?
 ?? ?if(val < fp->val)
 ?? ??? ?fp->left = s;
 ?? ?else
 ?? ??? ?fp->right = s;
 ?? ??? ?
 ?? ?//確定旋轉結點B,修改A的平衡因子
 ?? ?if(val < A->val)
 ?? ??? ?B = A->left;
 ?? ?else
 ?? ??? ?B = A->right;
?? ?A->height++;
 ?? ?
 ?? ?//修改路徑B到s的高度, B在A的下一層?
 ?? ?p = B; // p最終指向s, 之前指向的是s這一層,但是是空
?? ?while(p != s){
 ?? ??? ?p->height++;
 ?? ??? ?if(val < p->val)
 ?? ??? ??? ?p = p->left;?
 ?? ??? ?else
 ?? ??? ??? ?p = p->right;?? ?
 ?? ?}
 ?? ?//最終s的高度沒有++的 , 初始值賦為1?
 ?? ??? ?
 ?? ?
 ?? ?//調整完高度就修改結點和指針, 首先需要判斷失衡類型
 ?? ?//分為LL,LR,RR,RL
 ?? ?//結點A,B平衡因子在修改指針的過程中會變化,但是路徑上的結點不會
 ?? ?//指針修改包括s結點指針和原來雙親結點指針?
 ?? ?i = get_height(A->left) - get_height(A->right);
 ?? ?k = get_height(B->left) - get_height(B->right);?
 ?? ?
 ?? ?if(i == 2 && k == 1){//LL
 ?? ??? ?//B作為旋轉結點
 ?? ??? ?//先改結點指針, 此時s插入在B的左子樹下, 原來可以認為B左右子樹,A右子樹均為空
 ?? ??? ?A->left = B->right;
 ?? ??? ?B->right = A;
 ?? ??? ?
 ?? ??? ?//考慮原來A結點的指針,改成B后相應的指針也要改變,下面同理
 ?? ??? ?if(A->parent == NULL)
 ?? ??? ??? ?root = B;
 ?? ??? ?else if(A->parent->left == A)
 ?? ??? ??? ?A->parent->left = B;
 ?? ??? ?else
 ?? ??? ??? ?A->parent->right = B;?? ??? ?
 ?? ?}
 ?? ?else if(i == -2 && k == -1){//RR
 ?? ??? ?A->right = B->left;
 ?? ??? ?B->left = A;
 ?? ??? ?
 ?? ??? ?if(A->parent == NULL)
 ?? ??? ??? ?root = B;
 ?? ??? ?else if(A->parent->left == A)
 ?? ??? ??? ?A->parent->left = B;
 ?? ??? ?else
 ?? ??? ??? ?A->parent->right = B;?? ?
 ?? ?}
 ?? ?else if(i == 2 && k == -1){//LR
 ?? ??? ?//此時認為C的左右子樹空,B逆時針旋轉,A順時針旋轉, s插入C的左子樹或者右子樹?
 ?? ??? ?//如果C結點也是空,也就是說B右子樹空,那么直接插入C=s為B右子樹,此時A右子樹也是空的?
 ?? ??? ?C = B->right;
 ?? ??? ?B->right = C->left;
 ?? ??? ?A->left = C->right;
 ?? ??? ?C->left = B;
 ?? ??? ?C->right = A;
 ?? ??? ?
 ?? ??? ?if(A->parent == NULL)
 ?? ??? ??? ?root = C;
 ?? ??? ?else if(A->parent->left == A)
 ?? ??? ??? ?A->parent->left = C;
 ?? ??? ?else
 ?? ??? ??? ?A->parent->right = C;
 ?? ?}
 ?? ?else if(i == -2 && k == 1){//RL?
 ?? ??? ?//和LR一樣,畫圖來看就好
 ?? ??? ?C = B->left;
 ?? ??? ?A->right = C->left;
 ?? ??? ?B->left = C->right;
 ?? ??? ?C->left = A;
 ?? ??? ?C->right = B;
 ?? ??? ?
 ?? ??? ?if(A->parent == NULL)
 ?? ??? ??? ?root = C;
 ?? ??? ?else if(A->parent->left == A)
 ?? ??? ??? ?A->parent->left = C;
 ?? ??? ?else
 ?? ??? ??? ?A->parent->right = C;
 ?? ?}
 ?? ?return root;
 }
icoding復習8 堆排?
1. 堆輔助函數
二叉堆是完全二叉樹或者是近似完全二叉樹。二叉堆有兩種:最大堆和最小堆。
最大堆(大頂堆):父結點的鍵值總是大于或等于任何一個子節點的鍵值,即最大的元素在頂端;
 最小堆(小頂堆):父結點的鍵值總是小于或等于任何一個子節點的鍵值,即最小的元素在頂端。
 二叉堆子結點的大小與其左右位置無關。
 二叉堆一般用數組來表示。例如,根節點在數組中的位置是0,第n個位置的子節點分別在2n+1和 2n+2。
 因此,第0個位置的子節點在1和2,1的子節點在3和4。以此類推。這種存儲方式便于尋找父節點和子節點。
 在二叉堆上可以進行插入節點、刪除節點、取出值最小的節點、減小節點的值等基本操作。
“最小堆”的定義如下:
 typedef struct _otherInfo
 {
 ? ? int i;
 ? ? int j;
 }OtherInfo;
typedef struct _minHeapNode
 {
 ? ? int value;
 ? ? OtherInfo otherInfo;
 }MinHeapNode, *PMinHeapNode;
typedef struct _minPQ {
 ? ? PMinHeapNode heap_array; // 指向堆元素數組
 ? ? int heap_size; // 當前堆中的元素個數
 ? ? int capacity; ?//堆數組的大小
 }MinHeap, *PMinHeap;
 請實現最小堆的四個輔助函數:
int parent(int i); //返回堆元素數組下標為 i 的結點的父結點下標
 int left(int i); ?//返回堆元素數組下標為 i 的結點的左子結點下標
 int right(int i); ?//返回堆元素數組下標為 i 的結點的右子結點下標
 void swap_node(MinHeapNode *x, MinHeapNode *y); ?//交換兩個堆元素的值
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 重點區別
 1. PMinHeapNode heap_array
 2. MinHeapNode *heap_array
 3. MinHeapNode heap_array[capacity]//三者等價,前兩者沒分配空間
對于heap_array[i].value,記得點運算符, 因為這個不是指針了!!!!!
 ?
#include 
 #include 
 #include "minbinheap.h" // 請不要刪除,否則檢查不通過
int parent(int i){
 ?? ?return (i - 1) / 2;?
 }
 int left(int i){
 ?? ?return 2*i+1;
 }
 int right(int i){
 ?? ?return 2*i+2;
 }
 void swap_node(MinHeapNode *x, MinHeapNode *y){
 ?? ?int value;
 ?? ?int i, j;
 ?? ?
 ?? ?value = y->value;
 ?? ?i = y->otherInfo.i;
 ?? ?j = y->otherInfo.j;
 ?? ?
 ?? ?y->value = x->value;
 ?? ?y->otherInfo.i = x->otherInfo.i;
 ?? ?y->otherInfo.j = x->otherInfo.j;
 ?? ?
 ?? ?x->value = value;
 ?? ?x->otherInfo.i = i;
 ?? ?x->otherInfo.j = j;
 ?? ?
 ?? ?return;
 }
2. 堆初始化
 請實現最小堆的初始化函數:
void init_min_heap(PMinHeap pq, int capacity);
 其中 pq指向堆,capacity為堆元素數組的初始化大小。
void init_min_heap(PMinHeap pq, int capacity){//小根堆, 小的在上面?
 //題目沒有明確?
 //?? ?pq = (PMinHeap)malloc(sizeof(MinHeap));
 //?? ?if(!pq) return;
 ?? ?
 ?? ?if(!(pq->heap_array = (PMinHeapNode)malloc(sizeof(MinHeapNode) * capacity))){
 ?? ??? ?free(pq); return;
 ?? ?}
 ?? ?pq->capacity = capacity;
 ?? ?pq->heap_size = 0;
 ?? ?//return;
 }
 3. 堆化
 請實現最小堆的“堆化”函數:
 void min_heapify(PMinHeap pq, int i);
 其中 pq指向堆,i 為堆元素在數組中的下標。該函數假設元素i對應的子樹都已經是最小堆
 (符合最小堆的要求),但元素i為根的子樹并不是最小堆,
 min_heapify將對元素i及其子樹的各結點進行調整,使其為一個最小堆。
void min_heapify(PMinHeap pq, int i){
 ?? ?int j = parent(i);
 ?? ?//if(pq->heap_array[i].value > pq->heap_array[j].value) return;
 ?? ?for(; j >= 0 && pq->heap_array[i].value > pq->heap_array[j].value; i = j, j = parent(i))
 ?? ??? ?swap_node(&pq->heap_array[i], &(pq->heap_array[j]));
 }
void min_heapify(PMinHeap pq, int i){//一行?
 ?? ?for(int j = parent(i); j >= 0 && pq->heap_array[i].value > pq->heap_array[j].value; i = j, j = parent(i))swap_node(&(pq->heap_array[i]), &(pq->heap_array[j]));}
 ?? ?
 ?? ?
 4. 堆元素插入
 請實現最小堆的元素插入函數:
bool heap_insert_value(PMinHeap pq, int value);
 其中 pq指向堆,value 為要插入的堆元素。
bool heap_insert_value(PMinHeap pq, int value){//小根堆,子樹關鍵字大于等于根的關鍵字?
 ?? ?//if(pq->heap_size == pq->capacity - 1) return false;
 ?? ?
 ?? ?int i, j;
 ?? ?i = pq->heap_size;
 ?? ?j = parent(i);
 ?? ?pq->heap_array[i].value = value;
 ?? ?while(i){
 ?? ??? ?if(value < pq->heap_array[j].value){
 ?? ??? ??? ?swap_node(&(pq->heap_array[i]), &(pq->heap_array[j]));
 ?? ??? ??? ?i = j;
 ?? ??? ??? ?j = parent(i);
 ?? ??? ?}
 ?? ??? ?else{
 ?? ??? ??? ?pq->heap_size++;
 ?? ??? ??? ?return true;
 ?? ??? ?}
 ?? ?}?? ?
 }
5. 數組合并
 假設有 n 個長度為 k 的已排好序(升序)的數組,請設計數據結構和算法,
 將這 n 個數組合并到一個數組,且各元素按升序排列。即實現函數:
?void merge_arrays(const int* arr, int n, int k, int* output);
 其中 arr 為按行優先保存的 n 個長度都為 k 的數組,output 為合并后的按升序排列的數組,大小為 n×k。
時間要求(評分規則),當 n > k 時:
 滿分:時間復雜度不超過 O(n×k×log(n))
 75分:時間復雜度不超過 O(n×k×log(n)×k)
 59分:其它,如:時間復雜度為 O(n2×k2) 時。
#include
 #include
 //建立大根堆(大的在上面) ?
 void build_bigroot(int *a, int i, int size){
 //參數說明:a傳入的數組,i待排序元素開始位置,size數組元素個數(長度)
 ?? ?int j, s;
 ?? ?//j是i的孩子指針,s暫存排序的元素
 ?? ?
 ?? ?//不設監視哨,所以從'0'(i)開始排序,孩子為2*i+1?
 ?? ?j = 2 * i + 1;
 ?? ?s = a[i];
 ?? ?
 ?? ?while(j < size){//可以設置bool變量測試是否篩選完畢,減小時間復雜度?
 ?? ?//不可以取等哈 ,0開始的
 ?? ?
 ?? ??? ?//存在右子樹并且右子樹更大,那么篩選右子樹?
 ?? ??? ?if(j + 1 < size && a[j+1] > a[j])
 ?? ??? ??? ?j++;?
 ?? ??? ?
 ?? ??? ?if(s >= a[j])
 ?? ??? ??? ?break;
 ?? ??? ?else{//如果大的記錄在下,那么上浮?
 ?? ??? ??? ?a[i] = a[j];
 ?? ??? ??? ?i = j;
 ?? ??? ??? ?j = 2 * i + 1;
 ?? ??? ?}?
 ?? ?}
 ?? ?//最后把篩選完成后的數據放在合適位置
 ?? ?a[i] = s;?
 }
void merge_arrays(const int *arr, int n, int k, int* output){
 ?? ?//說明一下arr為const int類型,不能改動,所以只好浪費時空復制出來在變動
 ?? ?int i, size, x;
 ?? ?size = n * k;
 ?? ?int a[size];
 ?? ?//大根堆是大的在上,沒有什么其他要求.堆排后大的放大到最后,形成升序排列的小根堆?
 ?? ?
 ?? ?for(i= 0; i < size; i++)
 ?? ??? ?a[i] = arr[i];
 ?? ?//建立大根堆?
 ?? ?for(i = size / 2 -1; i >= 0; i--)
 ?? ??? ?build_bigroot(a, i , size);
 ?? ?//堆排?
 ?? ?for(i = size - 1; i >=1; i--){
 ?? ??? ?x = a[0];
 ?? ??? ?a[0] = a[i];
 ?? ??? ?a[i] = x;
 ?? ??? ?build_bigroot(a, 0, i);
 ?? ?}?
 ?? ?for(i = 0; i < size; i++)
 ?? ??? ?output[i] = a[i];
 }?
總結
以上是生活随笔為你收集整理的icoding复习7, 8的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 有道词典例句查询更直观
- 下一篇: 泰国英拉是哪里人
