算法刷题收获
算法设计思想总结
分治法:1.大问题拆分成规模相等的小问题;2.对小问题求解;3.合并小问题的解以得到原问题的解
递归法:将问题逐层分解,最终归结为最简单的问题
hash可以缩小问题规模,所以在大数据的算法设计下,hash算法较好
https://baike.baidu.com/item/bloom%20filter/6630926?fr=aladdin
大数据的算法设计:hash法,bit-map位图,bloom filter法
- 【完全背包问题】,其二是「背包方案数问题」,【不完全背包】
- 【数据结构的算法设计/一个元素具有2个以上状态--------算法设计时要优先考虑建立一个内部类来解决问题】如LFU缓存结构设计,N皇后问题
编程实战中常用的算法
英文句子中出现频率最高的单词
String sentence="to be or not to be,that is a question";
String[] wordList = sentence.split("[\\s|,]");
/**
* 使用toMap()函数之后,返回的就是一个Map了,自然会需要key和value。
toMap()的第一个参数就是用来生成key值的,
第二个参数就是用来生成value值的。
第三个参数用在key值冲突的情况下:(a,b)->a+b意思为a的value值加上b的value值作为冲突key的值;(a, b) -> a 意思就是以a的value值作为冲突key的值
*/
String key = Arrays.stream(wordList).collect(Collectors.toMap(k -> k, v -> 1, (a, b) -> a + b)).entrySet().stream().max((x, y) -> x.getValue() - y.getValue()).get().getKey();
/**
//方法1
List<Map.Entry<String,Integer>> list = new ArrayList(collect.entrySet());
Collections.sort(list, (o1, o2) -> (o2.getValue() - o1.getValue()));
list.get(0).getKey();
//方法2
String key = collect.entrySet().stream().sorted((k1, k2) ->k2.getValue()- k1.getValue() ).collect(Collectors.toList()).get(0).getKey();
System.out.println(key);
*/
hash表
hash数据结构底层可通过数组(通过hash函数将关键字转成数组下标)/链表实现,当然也可以通过数组+链表组合实现。
hash冲突的解决方案:(线性探测和二次探测必须考虑载荷因子,超过0.7-0.8就增容,增大效率,(以空间换时间)—数组长度为10,载荷为0.8,则当数组中有了8个元素时就需要对数组扩容)
2.链地址法。
1.开发定址法
线程探测:Index=Hash(key)+ i(i= 1、2、3……不算第一放入的数)
假设hash函数为:Hash(key)=key%10,此时如果我要添加key=2,经过hah算出index为2,它和58冲突了,则2+1,还冲突了,那就2+2=4,所以就将2放到index=4处
二次探测: Index=Hash(key)+ i^2(i= 1、2、3……不算第一放入的数)
58与18冲突,加1 ^ 2,到了index=9的位置,有冲突继续加2^2到了index=2的位置。
}
java api操作
//找到小于或等于key(a)的最大键 如果不存在就赋值为null
Map.Entry<Integer, Integer> result = map.floorEntry(a);
if(result==null) System.out.println(0);
else System.out.println( result.getValue() );
a
最优情况,hash不碰撞,O(1),典型情况,近似是O(1),因为几乎没有碰撞,最坏情况,O(N),也就是所有的hash都一样,那么退化为线性查找
- 对map的key进行操作
List<Date> dateList = map
.keySet()
.stream()
.sorted(Comparator.comparing(Date::getTime).reversed())
.collect(Collectors.toList());
map.get(dateList.get(4));
可以知对map中的键进行排序,将道键存在一个List中,然后根据map.getKey()获取值专,key通过之前存储的属List获取
- 获取map的长度
map.keySet().size();
对集合排序:可调用Collections.sort(List< Object >);
二分查找
4. 寻找两个有序数组的中位数
分治法的思想
题解----解法3
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int n = nums1.length;
int m = nums2.length;
//加入有9个元素,找中位数,那就是求(第4小的数+第5小的数)/2
int left = (n + m + 1) / 2;
int right = (n + m + 2) / 2;
//将偶数和奇数的情况合并,如果是奇数,会求两次同样的 k 。
return (getKth(nums1, 0, n - 1, nums2, 0, m - 1, left) + getKth(nums1, 0, n - 1, nums2, 0, m - 1, right)) * 0.5;
}
private int getKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k) {
int len1 = end1 - start1 + 1;
int len2 = end2 - start2 + 1;
//让 len1 的长度小于 len2,这样就能保证如果有数组空了,一定是 len1
if (len1 > len2) return getKth(nums2, start2, end2, nums1, start1, end1, k);
if (len1 == 0) return nums2[start2 + k - 1];
if (k == 1) return Math.min(nums1[start1], nums2[start2]);
int i = start1 + Math.min(len1, k / 2) - 1; //找数组1的第k'小的数
int j = start2 + Math.min(len2, k / 2) - 1;
if (nums1[i] > nums2[j]) {
//数组2的第k'小的数小,故排序掉num2的0到k'的数
return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
}
else {
return getKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1));
}
}
链表
反转单链表
public ListNode ReverseList(ListNode head) {
if(head==null) return null;
ListNode pre=null;
ListNode cur=head;
ListNode nxt=cur.next;
while(cur!=null){
cur.next=pre;
pre=cur;
cur=nxt;
if(cur!=null) nxt=cur.next;
}
return pre;
}
k 个一组翻转链表
ArrayDeque(双端队列)和linkedList(双端链表)----二者都可作栈/队列来使用
k 个一组翻转链表----递归/栈/尾插法
合并两个有序链表
面试题
凑硬币
硬币
【思路】
令 dp[i][j] 为遍历到当下这个硬币时,组成金额 j 的方法数目
有两种可能性(1)当前这个硬币没有取,dp[i][j]=dp[i-1][j];(2)当前这个硬币取了,dp[i][j]=dp[i][j-coins[i]]。最后的结果是两者的和
将状态转移方程翻译成代码,并处理边界条件
public int waysToChange(int n) {
int[] dp=new int[n+1];
int[] coins={1,5,10,25};
for(int i=0;i<=n;i++) dp[i]=1;
for(int j=1;j<=n;j++){
for(int i=1;i<4;i++){ //避免出现1+(1+1+1) 和 (1+1+1)+1+1被看成是2种情况。
if(j>=coins[i])
dp[j]=(dp[j]+dp[j-coins[i]])%1000000007;
}
}
return dp[n];
}
判定是否互为字符重排
-
异或运算:但有问题:【“acac”,“bbbb”-----返回的是true】
https://leetcode-cn.com/problems/check-permutation-lcci/solution/yi-huo-yun-suan-shi-jian-fu-za-du-onkong-jian-fu-z/ -
排序,再比较内容
class Solution {
public boolean CheckPermutation(String s1, String s2) {
char[] c1=s1.toCharArray();
Arrays.sort(c1);
char[] c2=s2.toCharArray();
Arrays.sort(c2);
return new String(c1).equals(new String(c2));
}
}
字符串压缩
public String compressString(String S) {
if (S == null || S.length() <= 2) {
return S;
}
StringBuilder sb = new StringBuilder().append(S.charAt(0));
int cnt = 1;
for (int i = 1; i < S.length(); i++) {
// 如果i与i-1相同,cnt累加
if (S.charAt(i) == S.charAt(i - 1)) {
cnt++;
} else {
// 否则拼接上i-1的次数,从i开始重新计数
sb.append(cnt).append(S.charAt(i));
cnt = 1;
}
}
return sb.append(cnt).length() < S.length()? sb.toString(): S;
}
是否是合法的二叉树:
class Solution {
long pre;
boolean flag;
public boolean isValidBST(TreeNode root) {
if (root==null) return true;
pre = Long.MIN_VALUE;
flag = true;
help(root);
return flag;
}
void help(TreeNode root) {
if (root == null || !flag) return;
help(root.left);
if (root.val > pre)
pre = root.val;
else
flag = false;
help(root.right);
}
}
LFU缓存
【数据结构的算法设计/一个元素具有2个以上状态--------算法设计时要优先考虑建立一个内部类来解决问题】
LFU缓存结构设计
class LFUCache {
Map<Integer, Node> cache; // 存储缓存的内容
Map<Integer, LinkedHashSet<Node>> freqMap; // 存储每个频次对应的双向链表
int size;
int capacity;
int min; // 存储当前最小频次
public LFUCache(int capacity) {
cache = new HashMap<>(capacity);
freqMap = new HashMap<>();
this.capacity = capacity;
}
public int get(int key) {
Node node = cache.get(key);
if (node == null) {
return -1;
}
freqInc(node);
return node.value;
}
//等于set方法
public void put(int key, int value) {
if (capacity == 0) {
return;
}
Node node = cache.get(key);
if (node != null) {
//缓存中已有,需要更新频率
node.value = value;
freqInc(node);
} else {
//缓存满了,移除缓存
if (size == capacity) {
Node deadNode = removeNode();
cache.remove(deadNode.key);
size--;
}
//缓存有空间,添加到缓存
Node newNode = new Node(key, value);
cache.put(key, newNode);
addNode(newNode);
size++;
}
}
void freqInc(Node node) {
// 从原freq对应的链表里移除, 并更新min
int freq = node.freq;
LinkedHashSet<Node> set = freqMap.get(freq);
set.remove(node);
if (freq == min && set.size() == 0) {
min = freq + 1;
}
// 加入新freq对应的链表
node.freq++;
LinkedHashSet<Node> newSet = freqMap.get(freq + 1);
if (newSet == null) {
newSet = new LinkedHashSet<>();
freqMap.put(freq + 1, newSet);
}
newSet.add(node);
}
void addNode(Node node) {
LinkedHashSet<Node> set = freqMap.get(1);
if (set == null) {
set = new LinkedHashSet<>();
freqMap.put(1, set);
}
set.add(node);
min = 1;
}
Node removeNode() {
LinkedHashSet<Node> set = freqMap.get(min);
Node deadNode = set.iterator().next();
set.remove(deadNode);
return deadNode;
}
public static void main(String[] args) {
LFUCache lruCache = new LFUCache(2);
lruCache.put(1, 1);
lruCache.put(2, 2);
System.out.println(lruCache.get(1));
}
}
class Node {
int key;
int value;
int freq = 1;
public Node() {
}
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}