算法刷题收获(2)

算法设计思想总结

分治法:1.大问题拆分成规模相等的小问题;2.对小问题求解;3.合并小问题的解以得到原问题的解
递归法:将问题逐层分解,最终归结为最简单的问题
hash可以缩小问题规模,所以在大数据的算法设计下,hash算法较好
https://baike.baidu.com/item/bloom%20filter/6630926?fr=aladdin
大数据的算法设计:hash法,bit-map位图,bloom filter法

  1. 【完全背包问题】,其二是「背包方案数问题」,【不完全背包】
  2. 【数据结构的算法设计/一个元素具有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];
    }

判定是否互为字符重排

  1. 异或运算:但有问题:【“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/

  2. 排序,再比较内容

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

幂集

orgin link
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值