算法学习(14)Hash的特征和队列基础

本文详细介绍了哈希存储原理,包括散列和映射的概念,以及处理碰撞的不同方法如开放地址法和链地址法。同时讲解了队列的基本特征和基于链表的实现,以Java、Python和C语言为例。
摘要由CSDN通过智能技术生成

1、Hash存储的原理

(1)哈希,也称散列,就是把任意长度的输入,通过散列算法,变换成固定长度的输出,这个输出值就是散列值。
(2)映射(访问的时间复杂度为O(1))
举例:
现在假设数组array存放的是1到15这些数字,将这些数字存在一个大小是7的Hash表中。该如何存?

存储的位置计算公式是:
index = number % 7

如图即为:
在这里插入图片描述
现在来取数据:
当测试13在不在这个结构里时,同样用上面的公式,13 % 7 = 6,直接访问array[6]这个位置,很明显是在的,返回true;
当测试20在不在这个结构里时,同样用上面的公式,20% 7 = 6,直接访问array[6]这个位置,很明显是不在的,返回false;
以上就是基本的映射。

2、Hash处理冲突的方法

概念:在上面的例子中,我们发现有些在Hash中很多位置可能要存两个甚至多个元素,这种两个不同的输入值,根据同一散列函数计算出的散列值相同的现象叫做碰撞。

解决方法:
(1)开放地址法(Java里的Threadlocal):
就是一旦发生冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将其记录存入。
如图所示:
在这里插入图片描述
Threadlocal 里有一个专门存储元素的ThreadlocalMap,这个还是要单独学习一下的,Hash处理相关问题的整个过程非常复杂。。。
(2)链地址法(java里的ConcurrentHashMap):
将哈希表的每个单元作为链表的头结点,所有哈希地址为i的元素构成一个同义词链表。即发生冲突时就把该关键字链在以该单元为头结点的链表的尾部。
如图:
在这里插入图片描述

注意:如图,数组的长度必须是是2的n次幂,并且它的entry不能大于数组长度的75%,如果大于就会触发扩容机制进行扩容。

HashMap的实现原理是先要找到要存放数组的下标,如果是空的就存进去,如果不是空的就判断key值是否一样,如果一样就替换,如果不一样就以链表的形式存在链表中(从JDK8开始,根据元素数量选择使用链表还是红黑树存储)。

3、队列存储的基本特征

先入队者先出队
后入队者后出队

4、基于链表来实现队列

在尾部(rear)进队列,在头部(front)出队列

Java代码(含在线编辑器测试):

import java.util.*;

class LinkQueue{
    private Node front;
    private Node rear;
    private int size;
    
    public LinkQueue(){
        this.front = new Node(0);
        this.front = new Node(0);
        
    }
    
    //入队
    public void push(int value){
        Node newNode = new Node(value);
        Node temp = front;
        while(temp.next != null){
            temp = temp.next;
        }
        temp.next = newNode;
        rear = newNode;
        size ++;
        
    }
    
    //出队
    public int pull(){
        if(front.next == null){
            System.out.println("队列已空");
            
        }
        Node firstNode = front.next;
        front.next = firstNode.next;
        size --;
        return firstNode.data;
        
    }
    
    //遍历队列
    public void traverse(){
        Node temp = front.next;
        while(temp != null){
            System.out.println(temp.data + "\t");
            temp = temp.next;
        }
        
    }
    
    static class Node{
        public int data;
        public Node next;
        public Node(int data){
            this.data = data;
        }
    }
    
}

public class Main
{
	public static void main(String[] args) {
	    LinkQueue LinkQueue = new LinkQueue();
	    LinkQueue.push(1);
	    LinkQueue.push(2);
	    LinkQueue.push(3);
		System.out.println("第一个出队的元素为:" + LinkQueue.pull());
		System.out.println("队列中的元素为:" );
		LinkQueue.traverse();
		
	}
}

Python代码(含本地编辑器测试):

class Queue(object):

    #感觉这个是基于数组的队列
    def __init__(self):
        self.items = []

    def is_empty(self):
        return self.items == []

    #入队列
    def enqueue(self, item):
        self.items.insert(0, item)

    #出队列
    def dequeue(self):
        return self.items.pop()

    def size(self):
        return len(self.items)

if __name__ == "__main__":
    q = Queue()
    q.enqueue("hello")
    q.enqueue("world")
    q.enqueue("yyy")
    print(q.size())
    print(q.dequeue())
    print(q.dequeue())
    print(q.dequeue())

C语言代码(含本地编辑器测试):

#include<bits/stdc++.h>
using namespace std;

typedef struct Node{
    int data;
    struct Node* next;
}Node;

typedef struct LinkQueue{
    Node* front;
    Node* rear;
    int size;
}LinkQueue;

LinkQueue* createLinkQueue(){
    LinkQueue* queue = (LinkQueue* )malloc(sizeof(LinkQueue));
    queue->front = NULL;
    queue->rear = NULL;
    queue->size = 0;
    return queue;
}

void push(LinkQueue* queue, int value){
    Node* newNode = (Node* )malloc(sizeof(Node));
    newNode->data = value;
    newNode->next = NULL;

    //队列为空时,头指针和尾指针都指向新结点
    if(queue->rear == NULL){ 
        queue->front = newNode;
        queue->rear = newNode;
    }
    //队列不为空时······
    else{
        queue->rear->next = newNode;
        queue->rear = newNode;
    }

    queue->size ++;
}

int pull(LinkQueue* queue){
    if(queue->front == NULL){
        printf("队列已空\n");
        return 0;
    }
    //队列是从尾结点进栈,从头结点出栈
    Node* firstNode = queue->front;
    int value = firstNode->data;
    queue->front = firstNode->next;
    free(firstNode);
    queue->size --;
    
    if(queue->front == NULL){
        queue->rear = NULL;
    }

    return value;
}

void traverse(LinkQueue* queue){
    Node* temp = queue->front;
    while(temp != NULL){
        printf("%d\t",temp->data);
        temp = temp->next;
    }
}

int main(){
    LinkQueue* queue = createLinkQueue();

    push(queue, 1);
    push(queue, 2);
    push(queue, 3);

    traverse(queue);

    int value = pull(queue);
    printf("\n出队的元素:%d\n",value);

    traverse(queue);

    system("pause");
    return 0;
}

本篇文章为原创,欢迎转载,请注明文章出处链接: https://blog.csdn.net/2301_79084755/article/details/136435543。技术类文章一般都有时效性,本人会不定期对自己的博客进行修正更新,因此请访问出处以查看本文的最新版本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值