个人通过CodeTop的刷过一些经典算法
目录
leetcode 146 LRU缓存
思路:使用LinkedHashMap,LinkedHashMap是一个会按照put顺序,为key排序的的一个hash链表。作为缓存,在get时,如果命中缓存,将key删除重新put到链表尾部;在put时,如果命中缓存,将key删除再重新put到链表尾部,如果未命中且链表长度已满,删除链表头节点。即新的数据插入链表尾部,旧的数据从链表头部删除。
class LRUCache {
private int capacity;
private Map<Integer,Integer> map;
public LRUCache(int capacity) {
this.capacity = capacity;
map = new LinkedHashMap<>();
}
public int get(int key) {
if(map.containsKey(key)){
moveToFirst(key);
return map.get(key);
}else{
return -1;
}
}
public void put(int key, int value) {
if(map.containsKey(key)){
moveToFirst(key);
map.put(key,value);
}else{
if(map.size() >= capacity){
map.remove(map.keySet().iterator().next());
}
map.put(key,value);
}
}
public void moveToFirst(int key){
int val = map.remove(key);
map.put(key,val);
}
}
leetcode 912 手撕快速排序
思路:本题主要考察的是快排的写法,有两种,一种是基于交换的快排,一种是堆排序。
class Solution {
public int[] sortArray(int[] nums) {
quickSort(nums,0,nums.length-1);
return nums;
}
public void quickSort(int[] nums,int L, int R){
if(L >= R) return;
int mid = (L + R) >> 1;
swap(nums,L,mid);
int pivot = nums[L];
int index = L;
for(int i = L+1; i <= R; i++){
if(nums[i] < pivot){
index++;
swap(nums,index,i);
}
}
swap(nums,L,index);
quickSort(nums,L,index - 1);
quickSort(nums,index + 1,R);
}
public void swap(int[] nums,int i, int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
leetcode 15 三数之和
思路:本题的难点在于如何去除重复的解,本题解使用排序+双指针。
1.先对数组进行排序。
2.从数组开头遍历获取一个值,如果值大于0,直接结束;如果小于0,判断是否与前一个值相同,相同则循环找下一个值,直到不同。
3.确定一个数之后,再通过双指针获取剩下两个数,左指针指向当前的下一个数,右指针指向数组的最后一个数
4.将三个数相加,和大于0,右指针左移;和小于0,左指针右移;等于0,将三个数加入结果集,同时左指针右移、右指针左移,继续获取解。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> lists = new ArrayList<>();
if(nums == null || nums.length < 3){
return lists;
}
Arrays.sort(nums);
for(int i = 0; i < nums.length; i++){
if(nums[i] > 0) break;
if(i > 0 && nums[i] == nums[i-1]) continue;
int L = i + 1;
int R = nums.length - 1;
while( L < R ){
if(L - 1 > i && nums[L-1] == nums[L]){
L++;
continue;
}
if(R + 1 < nums.length && nums[R+1] == nums[R]){
R--;
continue;
}
if(nums[i] + nums[L] + nums[R] == 0){
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[L]);
list.add(nums[R]);
lists.add(list);
L++;
R--;
}else if(nums[i] + nums[L] + nums[R] > 0){
R--;
}else{
L++;
}
}
}
return lists;
}
}
leetcode 53 最大子序和
思路:最大子序和考虑到需要是连续的,当前元素加上之前的和如果大于当前元素本身,就加入的子序和中,如果当前元素加上之前的和小于当前元素本身,不如直接从当前元素开始重新计算子序和。
class Solution {
public int maxSubArray(int[] nums) {
int result = nums[0];
int current = nums[0];
for(int i = 1; i < nums.length; i++){
if(current + nums[i] > nums[i]){
current += nums[i];
}else{
current = nums[i];
}
result = Math.max(current,result);
}
return result;
}
}
leetcode 33 搜索旋转排序数组
思路:使用二分查找,由于是将原本排序过的数组进行旋转,根据中点分成两段,两段之中一定有其中一段是有序的,所以这种情况也适用于二分查找。
二分不是单纯指从有序数组中快速找某个数,这只是二分查找的一个应用。
二分的本质是两段性,并非单调性。只要一段满足某个性质,另一段不满足某个性质,就可以使用二分。
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right){
int mid = (left + right) >> 1;
if(nums[mid] == target) return mid;
if(nums[0] <= nums[mid]){
if(target < nums[mid] && target >= nums[left]){
right = mid - 1;
}else{
left = mid + 1;
}
}else{
if(target > nums[mid] && target <= nums[right]){
left = mid + 1;
}else{
right = mid - 1;
}
}
}
return -1;
}
}
leetcode 25 K个一组反转链表
思路:本题考查的是翻转链表,不过是将整个链表拆分为n个分别进行翻转,同时还需要几个变量来记录翻转前后的节点。
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
if (k == 1){
return head;
}
ListNode dummy = new ListNode();
dummy.next = head;
ListNode pre = dummy;
ListNode end = dummy;
while (end.next != null) {
for (int i = 0; i < k && end != null; i++) end = end.next;
if (end == null) break;
ListNode start = pre.next;
ListNode next = end.next;
end.next = null;
pre.next = reverse(start);
start.next = next;
pre = start;
end = pre;
}
return dummy.next;
}
public ListNode reverse(ListNode head){
ListNode pre = null;
ListNode cur