leetcode刷题笔记(4)
主题:哈希表
(1)30. 串联所有单词的子串(错1)
一顿操作猛如虎,提交击败百分五;
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<Integer>();
Map<String,Integer> map = new HashMap<String,Integer>();
for(String str : words){
if(map.get(str) == null){
map.put(str,1);
}else{
map.put(str,map.get(str)+1);
}
}
int wlen = words[0].length();
int len = s.length();
for(int start= 0;start < len-wlen+1;start++){
int left = start;
int n = 0;
while(left+wlen <= len && n < words.length){
if(map.get(s.substring(left,left+wlen)) == null || map.get(s.substring(left,left+wlen)) == 0){
break;
}else{
map.put(s.substring(left,left+wlen), map.get(s.substring(left,left+wlen))-1);
left += wlen;
n++;
}
}
if(n == words.length){
res.add(start);
}
map.clear();
for(String str : words){
if(map.get(str) == null){
map.put(str,1);
}else{
map.put(str,map.get(str)+1);
}
}
}
return res;
}
}
题解思路:使用temp_map和,map做比较,而不用每遍历一次重新生成map;双指针
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
if (s == null || s.length() == 0 || words == null || words.length == 0) return res;
HashMap<String, Integer> map = new HashMap<>();
int one_word = words[0].length();
int word_num = words.length;
int all_len = one_word * word_num;
for (String word : words) {
map.put(word, map.getOrDefault(word, 0) + 1);
}
for (int i = 0; i < one_word; i++) {
int left = i, right = i, count = 0;
HashMap<String, Integer> tmp_map = new HashMap<>();
while (right + one_word <= s.length()) {
String w = s.substring(right, right + one_word);
right += one_word;
if (!map.containsKey(w)) {
count = 0;
left = right;
tmp_map.clear();
} else {
tmp_map.put(w, tmp_map.getOrDefault(w, 0) + 1);
count++;
while (tmp_map.getOrDefault(w, 0) > map.getOrDefault(w, 0)) {
String t_w = s.substring(left, left + one_word);
count--;
tmp_map.put(t_w, tmp_map.getOrDefault(t_w, 0) - 1);
left += one_word;
}
if (count == word_num) res.add(left);
}
}
}
return res;
}
}
(2)36. 有效的数独
我的思路:分别判断行、列、九宫格中的数字是否满足要求
class Solution {
public boolean isValidSudoku(char[][] board) {
for(int i = 0; i < 9; i++){
if(!hang(board,i)){
return false;
}
if(!lie(board,i)){
return false;
}
if(!nine(board,i)){
return false;
}
}
return true;
}
public boolean hang(char[][] board, int row){
HashSet set = new HashSet();
for(int i = 0; i < 9;i++){
if(board[row][i] == '.'){
continue;
}
if(!set.contains(board[row][i])){
set.add(board[row][i]);
}else{
return false;
}
}
return true;
}
public boolean lie(char[][] board, int col){
HashSet set = new HashSet();
for(int i = 0; i < 9;i++){
if(board[i][col] == '.'){
continue;
}
if(!set.contains(board[i][col])){
set.add(board[i][col]);
}else{
return false;
}
}
return true;
}
public boolean nine(char[][] board, int index){
HashSet set = new HashSet();
int row = index / 3;
int col = index % 3;
for(int i = row*3 + 0; i < row*3+3;i++){
for(int j = col*3+0; j < col*3+3;j++){
if(board[i][j] == '.'){
continue;
}
if(!set.contains(board[i][j])){
set.add(board[i][j]);
}else{
return false;
}
}
}
return true;
}
}
题解思路:用三个哈希表分别记录行、列、小九宫格每个数字出现的次数
class Solution {
public boolean isValidSudoku(char[][] board) {
int[][] rows = new int[9][9];
int[][] columns = new int[9][9];
int[][][] subboxes = new int[3][3][9];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
char c = board[i][j];
if (c != '.') {
int index = c - '0' - 1;
rows[i][index]++;
columns[j][index]++;
subboxes[i / 3][j / 3][index]++;
if (rows[i][index] > 1 || columns[j][index] > 1 || subboxes[i / 3][j / 3][index] > 1) {
return false;
}
}
}
}
return true;
}
}
(3)41. 缺失的第一个正数(错1)
题解思路:将负数变为n+1;将<=n的元素对应的位置变为负数;返回第一个大于0的下标+1;
class Solution {
public int firstMissingPositive(int[] nums) {
int n = nums.length;
for(int i = 0; i < n; i++){
if(nums[i] <= 0){
nums[i] = n + 1;
}
}
for(int i = 0; i < n; i++){
int num = Math.abs(nums[i]);
if(num <= n){
nums[num-1] = -Math.abs(nums[num-1]);
}
}
for(int i = 0; i < n; i++){
if(nums[i] > 0){
return i+1;
}
}
return n+1;
}
}
(4)49. 字母异位词分组(错1)
方法1:排序
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> map = new HashMap<String, List<String>>();
for (String str : strs) {
char[] array = str.toCharArray();
Arrays.sort(array);
String key = new String(array);
List<String> list = map.getOrDefault(key, new ArrayList<String>());
list.add(str);
map.put(key, list);
}
return new ArrayList<List<String>>(map.values());
}
}
方法2:计数
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
HashMap<String, List<String>> map = new HashMap<String, List<String>>();
for(String str : strs){
int[] temp = new int[26];
String t = "";
for(char c: str.toCharArray()){
temp[c-'a']++;
}
for(int i = 0; i < temp.length; i++){
t += (i+'a' + temp[i]);
}
List<String> list = new ArrayList<String>();
list = map.getOrDefault(t, list);
list.add(str);
map.put(t,list);
}
return new ArrayList<List<String>>(map.values());
}
}
(5)73. 矩阵置零
我的思路:先遍历一遍矩阵,记录0元素所在行和列,再分别将行和列改为0;
class Solution {
public void setZeroes(int[][] matrix) {
boolean[] col = new boolean[matrix[0].length];
boolean[] row = new boolean[matrix.length];
for(int i = 0; i < matrix.length; i++){
for(int j = 0; j < matrix[0].length; j++){
if(matrix[i][j] == 0){
row[i] = true;
col[j] = true;
}
}
}
for(int i = 0; i < matrix.length; i++){
if(row[i]){
for(int j = 0; j < matrix[0].length; j++){
matrix[i][j] = 0;
}
}
}
for(int j = 0; j < matrix[0].length; j++){
if(col[j]){
for(int i = 0; i < matrix.length; i++){
matrix[i][j] = 0;
}
}
}
}
}
(6)146. LRU 缓存
python
class LRUCache:
def __init__(self, capacity: int):
self.count = capacity
self.lis = []
self.dic = {}
def get(self, key: int) -> int:
if key in self.dic:
self.lis.remove(key)
self.lis.append(key)
return self.dic[key]
else:
return -1
def put(self, key: int, value: int) -> None:
if key in self.dic:
self.dic[key] = value
self.lis.remove(key)
self.lis.append(key)
else:
if self.count == 0:
self.lis.append(key)
del self.dic[self.lis.pop(0)]
self.dic[key] = value
else:
self.dic[key] = value
self.lis.append(key)
self.count -= 1
题解思路:哈希表+双向链表,使用hashmap存储key-value,同时使用双向链表管理key的出入
class LRUCache {
class DLinkedNode{
int key;
int value;
DLinkedNode prev;
DLinkedNode next;
public DLinkedNode(){
}
public DLinkedNode(int key, int value){
this.key = key;
this.value = value;
}
}
private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
private int size;
private int capacity;
private DLinkedNode head,tail;
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
head = new DLinkedNode();
tail = new DLinkedNode();
head.next = tail;
tail.prev = head;
}
public int get(int key) {
DLinkedNode node = cache.get(key);
if(node == null){
return -1;
}else{
moveToHead(node);
return node.value;
}
}
public void put(int key, int value) {
DLinkedNode node = cache.get(key);
if(node == null){
DLinkedNode newNode = new DLinkedNode(key,value);
cache.put(key,newNode);
addToHead(newNode);
size++;
if(size > capacity){
DLinkedNode tail = removeTail();
cache.remove(tail.key);
size--;
}
}else{
node.value = value;
moveToHead(node);
}
}
private void addToHead(DLinkedNode node){
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(DLinkedNode node){
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void moveToHead(DLinkedNode node){
removeNode(node);
addToHead(node);
}
private DLinkedNode removeTail(){
DLinkedNode res = tail.prev;
removeNode(res);
return res;
}
}
(7)139. 单词拆分
错误思路:使用一个boolean变量标记是否可以拆分;用一个函数递归查找;当遇到很长的重复变量时,超出时间限制;
题解思路:动态规划,从开头由短至长检索子串是否可以拆分,去接与前长度为j的字符串是否可以拆分,以及后面这段字符是否在列表内;
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
int len = s.length();
boolean[] dp = new boolean[len+1];
dp[0] = true;
for(int i = 0; i <= len; i++){
for(int j = 0; j <= i; j++){
if(dp[j] && wordDict.contains(s.substring(j,i))){
dp[i] = true;
}
}
}
return dp[len];
}
}
(8)202. 快乐数
我的思路:使用一个列表记录前面出现过的数字,如果再次出现,说明进入死循环false。
class Solution {
public boolean isHappy(int n) {
if(n == 1){
return true;
}
ArrayList<Integer> list = new ArrayList<Integer>();
int temp = n;
while(temp != 1){
String s = (temp +"");
int record = 0;
for(int i = 0; i < s.length(); i++){
record += (s.charAt(i)-'0')*(s.charAt(i)-'0');
}
temp = record;
if(list.contains(temp)){
return false;
}
list.add(temp);
}
return true;
}
}
题解其他思路:快慢指针法;
(9)147. 对链表进行插入排序
我的思路:从前往后插入。先遍历一遍获得链表长度len,将执行len-1次插入,使用temp复制一个要插入的节点,将原来的节点删除,每次插入先移动到有序链表的前面,再进行比较,如果temp大于当前节点,且下一个节点不为null,后移,如果temp小于当前节点,将其插入该节点前,否则插入节点后。
class Solution {
public ListNode insertionSortList(ListNode head) {
if(head == null && head.next == null){
return head;
}
ListNode dummy = new ListNode(0,head);
ListNode pre = dummy;
ListNode cur = head;
int len = 0;
while(cur != null){
cur = cur.next;
len++;
}
for(int i = 0; i < len-1; i++){
int moveCount = len-i-2;
ListNode temp = new ListNode(dummy.next.val);
dummy.next = dummy.next.next;
pre = dummy;
cur = dummy.next;
while(moveCount > 0){
pre = pre.next;
cur = cur.next;
moveCount--;
}
while(cur != null && cur.next != null && temp.val > cur.val){
pre = pre.next;
cur = cur.next;
}
if(temp.val <= cur.val){
temp.next = cur;
pre.next = temp;
}else{
cur.next = temp;
}
}
return dummy.next;
}
}
(10)106. 从中序与后序遍历序列构造二叉树(错1)
class Solution {
int post_idx;
int[] postorder;
int[] inorder;
Map<Integer,Integer> idx_map = new HashMap<Integer, Integer>();
public TreeNode buildTree(int[] inorder, int[] postorder) {
this.inorder = inorder;
this.postorder = postorder;
post_idx = postorder.length-1;
int idx = 0;
for(Integer val: inorder){
idx_map.put(val,idx++);
}
return findRoot(0,inorder.length-1);
}
public TreeNode findRoot(int in_left, int in_right){
if(in_left > in_right){
return null;
}
int root_val = postorder[post_idx];
TreeNode root = new TreeNode(root_val);
int index = idx_map.get(root_val);
post_idx--;
root.right = findRoot(index+1, in_right);
root.left = findRoot(in_left, index-1);
return root;
}
}