1)字符串升序旋转
2)LRU实现
3)给定一个非空的整数数组,返回其中出现频率前 k 高的元素
4)36进制加法
5)数组中出现次数超过数组一半的数字
一:
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例 1:输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
package com.lzg.flume.intercepter;
public class SearchInRotatedSortedArray {
// 1 最外层的mid 跟 target进行比较
// 2 数组只可能是这两种类型// 456789123 // 789123456 进行比较
public int search(int[] nums, int target) {
if (nums == null || nums.length <= 0) {
return -1;
}
int len = nums.length;
int start = 0;
int end = len - 1;
while (start <= end) {
int mid = (start + end) >> 1;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target){
// mid在左侧递增大区间
if (nums[mid] > nums[end]) {// 456789123
// 目标值大于最右侧值,左侧寻找
if (target > nums[end]) {
end = mid - 1;
// 否则右侧寻找
} else {
start = mid + 1;
}
// mid在小区间, 此时mid又大于target,向左侧寻找
} else {// 789123456
end = mid - 1;
}
} else {
// mid在左侧递增大区间
if (nums[mid] > nums[end]) {// 456789123
// 右侧寻找
start = mid + 1;
// mid在小区间, 此时mid又小于target
} else { // 789123456
if (target > nums[end]) {
// 左侧寻找
end = mid - 1;
// 否则右侧寻找
} else {
start = mid + 1;
}
}
}
}
return -1;
}
public static void main(String[] args) {
int[] nums = {4,5,6,7,0,1,2};
System.out.println(new SearchInRotatedSortedArray().search(nums, 0));
System.out.println(new SearchInRotatedSortedArray().search(nums, 3));
}
}
题目二:Java实现LRU缓存
方法1:LRU Cache的链表+HashMap实现
class Node {
public int key, val;
public Node next, prev;
public Node(int k, int v) {
this.key = k;
this.val = v;
}
}
class DoubleList {
// 在链表头部添加节点 x,时间 O(1)
public void addFirst(Node x);
// 删除链表中的 x 节点(x 一定存在)
// 由于是双链表且给的是目标 Node 节点,时间 O(1)
public void remove(Node x);
// 删除链表中最后一个节点,并返回该节点,时间 O(1)
public Node removeLast();
// 返回链表长度,时间 O(1)
public int size();
}
class LRUCache {
// key -> Node(key, val)
private HashMap<Integer, Node> map;
// Node(k1, v1) <-> Node(k2, v2)...
private DoubleList cache;
// 最大容量
private int cap;
public LRUCache(int capacity) {
this.cap = capacity;
map = new HashMap<>();
cache = new DoubleList();
}
public int get(int key) {
if (!map.containsKey(key))
return -1;
int val = map.get(key).val;
// 利用 put 方法把该数据提前
put(key, val);
return val;
}
public void put(int key, int val) {
// 先把新节点 x 做出来
Node x = new Node(key, val);
if (map.containsKey(key)) {
// 删除旧的节点,新的插到头部
cache.remove(map.get(key));
cache.addFirst(x);
// 更新 map 中对应的数据
map.put(key, x);
} else {
if (cap == cache.size()) {
// 删除链表最后一个数据
Node last = cache.removeLast();
map.remove(last.key);
}
// 直接添加到头部
cache.addFirst(x);
map.put(key, x);
}
}
}
方法2:LRU Cache的LinkedHashMap实现
public class LRUCache3<K, V> {
private final int MAX_CACHE_SIZE;
private final float DEFAULT_LOAD_FACTOR = 0.75f;
LinkedHashMap<K, V> map;
public LRUCache3(int cacheSize) {
MAX_CACHE_SIZE = cacheSize;
//根据cacheSize和加载因子计算hashmap的capactiy,+1确保当达到cacheSize上限时不会触发hashmap的扩容,
int capacity = (int) Math.ceil(MAX_CACHE_SIZE / DEFAULT_LOAD_FACTOR) + 1;
map = new LinkedHashMap(capacity, DEFAULT_LOAD_FACTOR, true) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_CACHE_SIZE;
}
};
}
public synchronized void put(K key, V value) {
map.put(key, value);
}
public synchronized V get(K key) {
return map.get(key);
}
public synchronized void remove(K key) {
map.remove(key);
}
public synchronized Set<Map.Entry<K, V>> getAll() {
return map.entrySet();
}
public synchronized int size() {
return map.size();
}
public synchronized void clear() {
map.clear();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Map.Entry entry : map.entrySet()) {
sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue()));
}
return sb.toString();
}
}
题目三:
* 给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
* 示例 1:
* 输入: nums = [1,1,1,2,2,3], k = 2
* 输出: [1,2]
* 示例 2:
* 输入: nums = [1], k = 1
* 输出: [1]
* 说明:
* 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
* 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
方法1优先队列(利用优先队列进行排序,排序到第k个值之后,每次都取头上的最小数值跟剩下的依次比较,如果比最小的大就放入优先队列中。注意优先队列一直保持为k个数值)
public List<Integer> topKFrequent(int[] nums, int k) {
final HashMap<Integer, Integer> map = new HashMap<>();
//用map统计各个元素都有多少个
for (int num : nums) {
if (map.containsKey(num)) {
map.put(num, map.get(num) + 1);
} else {
map.put(num, 1);
}
}
//用优先队列获得最多的前K个
PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
public int compare(Integer a, Integer b) {
// 出现次数少的在前
return map.get(a) - map.get(b);
}
});
for (int key : map.keySet()) {
// 控制队列元素数量
if (queue.size() < k) {
queue.add(key);
continue;
}
// 队列中元素达到 K 值 ,去除最小值比较
int small = queue.peek();
if (map.get(small) < map.get(key)) {
queue.poll();
queue.add(key);
}
}
return new ArrayList<Integer>(queue);
}
方法2:桶排序(以数组中某个数字的个数作为bucket的index)
public List<Integer> topKFrequent2(int[] nums, int k) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
if (map.containsKey(num)) {
map.put(num, map.get(num) + 1);
} else {
map.put(num, 1);
}
}
// 创建 nums.length + 1 个桶
List<Integer>[] bucket = new List[nums.length + 1];
// 遍历map,根据value值 出现的次数 放到对应的桶中
for (Map.Entry<Integer, Integer> e : map.entrySet()) {
Integer value = e.getValue();
if (bucket[value] == null) {
bucket[value] = new ArrayList<>();
}
bucket[value].add(e.getKey());
}
List<Integer> freList = new ArrayList<>();
// 桶的编号表示出现次数,所以倒数桶
for (int j = bucket.length - 1; j > -1 && freList.size() < k; j--) {
if (bucket[j] != null)
freList.addAll(bucket[j]);
}
return freList;
}
四 36进制加法
36进制由0-9,a-z,共36个字符表示,最小为'0'
'0''9'对应十进制的09,'a''z'对应十进制的1035
例如:'1b' 换算成10进制等于 1 * 36^1 + 11 * 36^0 = 36 + 11 = 47
要求按照加法规则计算出任意两个36进制正整数的和
如:按照加法规则,计算'1b' + '2x' = '48'
要求:不允许把36进制数字整体转为10进制数字,计算出10进制数字的相加结果再转回为36进制
思路:
创建一个字符串,
string nums = "0123456789abcdefghijklmnopqrstuvwxyz";
找到两个字符串相加字母 在 字符串nums 中的位置, 这个位置就是字母所表示的大小,例如z:35. a:10
然后用位置相加,如果相加的结果超过36,则进位;否则不进位。
最后把剩下的字符串加到保存的结果中,翻转结果即可。
package com.lzg.flume.intercepter;
public class Plus6 {
public static String nums = "0123456789abcdefghijklmnopqrstuvwxyz";
public static String plus(String s1, String s2){
int i = s1.length()-1;
int j = s2.length()-1;
int temp =0;
StringBuilder sb = new StringBuilder();
while(i>=0&&j>=0){
char c1= s1.charAt(i);
char c2= s2.charAt(j);
int index1 = nums.indexOf(c1);
int index2 = nums.indexOf(c2);
int sum = index1+index2+temp;
if(sum>=36){
temp=1;
sb.append(nums.charAt(sum%36));
}else{
temp = 0;
sb.append(nums.charAt(sum%36));
}
i--;
j--;
}
while(i>=0){
char c1 = s1.charAt(i);
int index1 = nums.indexOf(c1);
int sum = temp+index1;
if(sum>=36){
temp=1;
sb.append(nums.charAt(sum%36));
}else{
temp=0;
sb.append(nums.charAt(sum%36));
}
i--;
}
while(j>=0){
char c2 = s2.charAt(j);
int index2 = nums.indexOf(c2);
int sum = temp+index2;
if(sum>=36){
temp=1;
sb.append(nums.charAt(sum%36));
}else{
temp=0;
sb.append(nums.charAt(sum%36));
}
j--;
}
if(temp!=0){
sb.append("1");
}
sb.reverse();
return sb.toString();
}
public static void main(String[] args) {
String s1= "1z";
String s2= "1";
System.out.println(plus(s1,s2));
}
}
五:数组中出现次数超过数组一半的数字
方法一:使用哈希表对数组每个数字进行遍历,Map<nums的某个值,出现次数>,当出现次数超过数组一半返回该数。
方法二:若一个数字出现的次数超过数组长度的一半,则该数字减去其他数字个数必定大于0,根据这个原理,使用摩尔法投票法,记录一个数字,遍历数组,若是该数,票数加一,不是减一,为零下次出现什么都投一票,记录值变为下次出现的值。最后,记录中必定是出现过半的数字!
class Solution {
public int majorityElement(int[] nums) {
if (nums.length == 1) return nums[0];
int half = nums.length >> 1;
int count;
Map<Integer, Integer> myMap = new HashMap<>();
for (int x = nums.length - 1; x > -1; x--) {
if (myMap.containsKey(nums[x])) {
count = myMap.get(nums[x]);
if (++count >= half) {
return nums[x];
}
myMap.remove(nums[x]);
myMap.put(nums[x], count++);
} else {
myMap.put(nums[x], 1);
}
}
return -1;
}
}
方法2
class Solution {
public int majorityElement(int[] nums) {
int result = nums[0];
int count = 0;
for (int x : nums) {
if (count == 0) {
count++;
result = x;
}
else {
count+=(x==result)?1:-1;
}
}
return result;
}
}