Hash的特征和队列基础
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。技术类文章一般都有时效性,本人会不定期对自己的博客进行修正更新,因此请访问出处以查看本文的最新版本。