LeetCode刷题:数据结构篇(Java|C++实现)

用到一些典型数据结构的题将在此处更新。

一、哈希表

笔记

1.什么是哈希表
哈希表离不开哈希函数,哈希函数就是根据key计算出应该存储地址的位置,而哈希表是基于哈希函数建立的一种查找表。
2.哈希函数构造方法

(1)直接定址法
取关键字或关键字的某个线性函数值为哈希地址。即H(key)=key 或 H(key)=a*key+b (a,b为常数)。
(2)数字分析法
若关键字是以R为基的数(如:以10为基的十进制数),并且哈希表中可能出现的关键字都是事先知道的,则可取关键字的若干数位组成哈希地址.
(3)平方取中法(常用)
取关键字平方后的中间几位为哈希地址。
(4)折叠法(适用于关键字位数比较多,且关键字中每一位上数字分布大致均匀的情况)
将关键字分割成位数相同的几部分(最后一部分的位数可不同),然后取这几部分的叠加和(舍去进位)作为哈希地址。
(5)除留余数法
取关键字被某个不大于哈希表表长m的数p除后所得余数为哈希地址(p为素数)
(6)随机数法(适用于关键字长度不等时)
选择一个随机函数,取关键字的随机函数值为它的哈希地址。即H(key)=random(key),其中random为随机函数。


实际应用时根据情况不同选用的哈希函数不同,通常,考虑因素如下:

  • 计算哈希函数所需时间(包括硬件指令的因素)
  • 关键字的长度
  • 哈希表的大小
  • 关键字的分布情况
  • 记录的查找频率

3.哈希冲突
哈希函数用不同key值却产生相同的地址,即H(key1)=H(key2)
如何解决?

(1)开放寻址法
所有输入的元素全部存放在哈希表里。当发生冲突时,使用某种探查技术在散列表中形成一个探查序列。沿此序列逐个单元地查找,直到找到给定的关键字,或者碰到一个开放的地址(即该地址单元为空)为止,插入只需将待插入的新结点存人该地址单元即可。主要有三种探查方法:

  • 线性探测
  • 平方探测
  • 伪随机探测

(2)链表法
将所有哈希地址相同的结点链接在同一个单链表中。
(3)公共溢出区法
建立一个特殊存储空间,专门存放冲突的数据。此种方法适用于数据和冲突较少的情况。
(4)再哈希法
准备若干个哈希函数,产生冲突时就采用另一个哈希函数,直到没有冲突。

相关题目

1. 两数之和

很容易想到使用暴力枚举的方法,先选定一个数,然后逐个判断剩下的数和否和选定的组成target,可以就返回对应的下标即可。单这样时间复杂度为O(n2),看了下数组长度,最长有10000,必定超时。因此自然想到优化下。事实上,其实遍历一次我们即可知道所有数据,如果我们记忆这些数据,在遇到下一个新的数是,就回忆下之前的某个数能否和其组成target,这样,只需遍历一次数据即可得到结果。那么怎么记忆?这里就用到哈希表。

  • Java
class Solution {
    public int[] twoSum(int[] nums, int target) {
		//用哈希表存储nums和下标的映射关心
    	Map<Integer, Integer> map=new HashMap<>();
    	int a[]=new int[2];
    	for (int i = 0; i < nums.length; i++) {
			int diff = target-nums[i];
			if (map.containsKey(diff)) {
				a[0]=map.get(diff);
				a[1]=i;
				return a;
			}
			map.put(nums[i], i);
		}
    	return a;
    }
}
  • C++
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> m;  //第一个数是key,key可以作为索引
        for (int i = 0; i < nums.size(); i++)
        {
            int diff = target - nums[i];
            if (m.count(diff))  //判断哈希表是不是存在这个差值
                return { m[diff],i }; //返回他们的下标                 
            m[nums[i]] = i;  //扩展哈希表
        }
        return {};
    }
};

二、栈

笔记

栈是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针。

相关题目

20.有效括号

根据题目的有效性描述,左右括号必须匹配,且括号内的括号也是有效的才行,因此合法的字符串必定包含紧紧挨在一起的同类型括号,且消除此对括号又能找到下一对同类型括号。重复上述操作最终可以使字符串为空。怎么消除呢?充分利用栈先进后出的性质,左括号直接进栈,如果下一个括号与栈顶括号匹配,就将栈顶括号出栈,实现消除,若不匹配,说明非法,返回假即可。

  • Java
static class Solution {
    public boolean isValid(String s) {
    	Stack<Character> stack = new Stack<>();
    	char c[]=s.toCharArray();
    	for (int i = 0; i < c.length; i++) {
    	    //左括号直接进栈
			if (stack.isEmpty()||c[i]=='('||c[i]=='['||c[i]=='{') {
				stack.push(c[i]);
				continue;
			}
			//遇到右括号就进行匹配验证
			if (!stack.isEmpty()) {
				if ((c[i]==')'&&stack.peek()=='(')||
					(c[i]==']'&&stack.peek()=='[')||
					(c[i]=='}'&&stack.peek()=='{')) stack.pop();
				else return false;
			}
		}
		//最后栈是空的,说明合法
    	return stack.isEmpty();	
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hilbob

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值