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。
它是一种高度平衡的二叉排序树。现二叉平衡树结点定义如下:

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];

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/311450.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python加载模型包占用内存多大_加载pickle python对象会占用大量内存

我有一个python的pickle对象,它生成一个180 Mb的文件.当我取消它时,内存使用量会爆炸到2或3Gb.你有类似的经历吗&#xff1f;这是正常的吗&#xff1f;对象是包含字典的树&#xff1a;每个边是一个字母,每个节点都是一个潜在的单词.因此,要存储一个单词,您需要的边数与该单词的…

《C++ Primer》1.52节练习

练习1.23 #include <iostream> #include "Sales_item.h"using namespace std;int main() {Sales_item trans1, trans2;cout << "请输入若干销售记录:" << endl;if (cin >> trans1) {int num 1;while (cin >> trans2)if (t…

ASP.NET Core 反向代理部署知多少

引言最近在折腾统一认证中心&#xff0c;看到开源项目[IdentityServer4.Admin&#xff1a;https://github.com/skoruba/IdentityServer4.Admin]集成了IdentityServer4和管理面板&#xff0c;就直接拿过来用了。在尝试Nginx部署时遇到了诸如虚拟目录映射&#xff0c;请求头超长、…

函数传参string_JavaScript 高阶函数入门浅析

原文&#xff1a;https://www.freecodecamp.org/news/a-quick-intro-to-higher-order-functions-in-javascript-1a014f89c6b/译者&#xff1a;jingruzhang校对者&#xff1a;acusp高阶函数高阶函数可以接收函数作为参数&#xff0c;同时也可以返回一个新的函数。高阶函数之所以…

all()与any()

all():当可迭代对象为空时返回True。或者当可迭代对象中是否所有值都为True&#xff0c;所有值都为True,则返回True。否则返回False。any():当可迭代对象为空时返回False。或者当可迭代对象中是否存在一个为True的值&#xff0c;若存在&#xff0c;返回True,否则返回False 示例…

.NET Core开发实战(第13课:配置绑定:使用强类型对象承载配置数据)--学习笔记...

13 | 配置绑定&#xff1a;使用强类型对象承载配置数据要点&#xff1a;1、支持将配置值绑定到已有对象2、支持将配置值绑定到私有属性上继续使用上一节代码首先定义一个类作为接收配置的实例class Config {public string Key1 { get; set; }public bool Key5 { get; set; }pub…

Python--第3次平时作业

目录 一、单项选择题 二、程序填空题 三、所有测试代码如下: 一、单项选择题 题号 1 2 3 4 5 6 7 8 9 10 答案 C A A C C D D D D C 题号 11 12 13 14 15 16 17 18 19 20 答案 A A C C B C A C B A 题号 21 22 23 24 25 …

工业互联网白皮书_发布|《工业互联网平台安全白皮书(2020)》发布

12月4日&#xff0c;2020年中国工业信息安全大会暨全国工控安全深度行(京津冀站)在北京国际会议中心举行。大会由国家工业信息安全发展研究中心、工业信息安全产业发展联盟主办&#xff0c;以“贯彻总体国家安全观&#xff0c;把牢工控安全基准线”为主题。会上&#xff0c;国家…

UVA - 514 Rails-栈

某城市有一个火车站&#xff0c;铁轨铺设如图6-1所示。 有n节车厢从A方向驶入车站&#xff0c;按进站顺 序编号为1&#xff5e;n。 你的任务是判断是否能让它们按照某种特定的顺序进入B方向的铁轨并驶出 车站。 例如&#xff0c;出栈顺序(5 4 1 2 3)是不可能的&#xff0c;但(5…

全局思维

在这个复杂多变的时代&#xff0c;是时候提升我们的思维了&#xff0c;树立大局意识&#xff0c;在把握空间纵轴线和时间水平线中思考和谋划大局。全局思维能力&#xff0c;蕴含着从全局的、长远的、战略的高度来分析问题和解决问题的能力&#xff1b;是善于从大处着眼、小处着…

python中函数的参数类型( 位置参数、关键字参数、默认值参数和可变长度参数)

目录 位置参数: 关键字参数: 默认值参数: 可变长度参数: 1.元组可变长度参数 : 2.字典可变长度参数: 附上练习代码: 位置参数: 实参与形参个数完全相同,按位置按顺序将实参传递给形参 def f(x, y):print(x, y) f(2, 3) 2, 3 关键字参数: 在函数调用中使用关键字参数&…

python分布式存储文件_python如何分布式存储文件的方法

想了很久&#xff0c;还是跟大家聊一聊关于分布式吧&#xff0c;只是因为大家在编写代码&#xff0c;填充内容时候&#xff0c;最多肯定是涉及文字以及图片&#xff0c;因此对于这些内容后期做代码存储肯定至关重要&#xff0c;没有任何一个用户会直接看代码来认知你的产品&…

UVA-11988 悲剧文本-静态链表

你有一个破损的键盘。键盘上的所有键都可以正常工作&#xff0c;但有时Home键或者End键会自 动按下。你并不知道键盘存在这一问题&#xff0c;而是专心地打稿子&#xff0c;甚至连显示器都没打开。当你 打开显示器之后&#xff0c;展现在你面前的是一段悲剧的文本。你的任务是在…

ASP.NET Core Razor 视图预编译、动态编译

0x01 前言ASP.NET Core在默认发布情况下&#xff0c;会启动预编译将试图编译成xx.Views.dll,也许在视图中打算修改一处很细小的地方我们需要再重新编译视图进行发布。下面我将从 ASP.NET Core 3 之前版本到 ASP.NET Core 3X 之后版本的一个配置列下下方供大家参考。0x02 预编译…

最大公约数,最小公倍数,质因式分解

目录 简单代码算出最大公约数,最小公倍数: 辗转相除法得到最大公约数: 两数相乘倒序最小公倍数: 分解质因式, 打印输出, 并且存到列表 主程序: 由质因式得到最小公倍数: 简单代码算出最大公约数,最小公倍数: # 最大公约数和最小公倍数 a int(input(please enter 1st num:)…

群晖python套件包_利用群晖Docker安装ubuntu16.04搭建python网站服务器(部署篇)

在帖子《利用群晖Docker安装ubuntu16.04搭建python网站服务器(安装篇)(地址&#xff1a;http://www.cirdown.com:81/thread-185-1-1.html)》中介绍了在群晖docker中如何安装和配置ubuntu容器&#xff0c;那么服务器系统有了&#xff0c;这篇我们就来讲部署篇&#xff0c;因为我…

《C++ Primer》2.1.2节练习

练习2.3 #include <iostream> using namespace std;int main() {unsigned u 10, u2 42;cout << u2 - u << endl;cout << u - u2 << endl;int i 10, i2 42;cout << i2 - i << endl;cout << i - i2 << endl;cout <…

从未来看 C#

前言如今 C# 虽然发展到了 8.0 版本&#xff0c;引入了诸多的函数式特性&#xff0c;但其实在 C# 未来的规划当中&#xff0c;还有很多足以大规模影响现有 C# 代码结构和组成的特性&#xff0c;本文中将会对就重要的特性进行介绍&#xff0c;并用代码示例展示这些特性。以下特性…

Python--第1次平时作业

目录 一、单项选择题 二:填空题 三:大题代码 四:题目 五:测试代码 一、单项选择题 题号 1 2 3 4 5 6 7 8 9 10 答案 D C C D A B A B A C 题号 11 12 13 14 15 16 17 18 19 20 答案 C D D B C B A A D B 题号 21 22 23 2…