大致套路
求总共多少 - 用分治法
求最大 - 用动态规划
求是否存在 - 用二分(先排序)
数组和字符串 - 大部分可以用双指针法
哈希可以降低时间复杂度
对数字的操作就是 n % 10 n/10
数字判断回文,反转后和原来相等
二分查找
public int binarySearch(int[] a, int l,int r, int k)
{
while(l <= r){ //要考虑搜索第一个或最后一个元素 l=r
int mid = l + (r-l)>>1; //(l+r)/2 可能造成溢出
if(a[mid] == k){
return mid;
}
if(a[mid] > k){
r = mid - 1;
}else{
l = mid + 1;
}
}
return -1;
}
双指针
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
套路:1. 先创建双指针 2.根据条件移动左指针 3.移动右指针。
class Solution {
public int removeDuplicates(int[] a) {
int i = 0;
for(int j = 1;j < a.length; j++){
if(a[j] != a[i]){
i++;
a[i] = a[j];
}
}
return i+1;
}
}
链表删除所有指定节点
套路:创建虚拟头节点
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
//创建虚拟节点
ListNode tmp = new ListNode(val - 1);
tmp.next = head;
ListNode cur = tmp;
while(cur.next != null) {
if(cur.next.val == val) {
cur.next = cur.next.next;
}else{
cur = cur.next;
}
}
return tmp.next; // 不返回head是因为 移除头指针时 返回head相当于没有移除
}
}
遍历链表
套路:先解放头指针
LinkNode cur = head;
while(cur != null)
{
sout(cur.val);
cur = cur.next;
}
归并排序
套路:
1. 排序左数组
2. 排序右数组
3. 组合归并(从头比较左右数组,小的放入临时数组)
class Solution {
public void mergeSort(int[] a,int s,int e,int[] tmp){
if(s >= e){
return;
}
int mid = s + ((s-e) >> 1);
mergeSort(a,s,mid,tmp);
mergeSort(a,mid+1,e,tmp);
merge(a,s,mid,e,tmp);
}
public void merge(int[] a,int s,int mid,int e,int[] tmp) {
int l = s;
int r = mid + 1;
int k = s;
while(l <= mid && r <= e){
tmp[k++] = a[l] > a[r] ? a[r++] : a[l++];
}
while(l <= mid){
tmp[k++] = a[l++];
}
while(r <= e){
tmp[k++] = a[r++];
}
//将tmp数组数据拷贝到a数组对应下标
while(s <= e){
a[s] = tmp[s++];
}
}
}
滑动窗口(本质就是双指针)
套路:1. 声明首尾指针和map 2. 根据map中的值移动头指针 3. 放入尾指针 并后移
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
class Solution {
public int lengthOfLongestSubstring(String ss) {
Map<Character,Integer> map = new HashMap(16);
int ans = 0;
for(int s = 0,e = 0;e < ss.length();e++){
char c = ss.charAt(e);
if(map.containsKey(c)){
s = Math.max(s, map.get(c)+1);
}
map.put(c,e);
ans = Math.max(ans,e-s+1);
}
return ans;
}
}
递归
经典的汉诺塔问题。
public static void main(String[] args){
hanio(3,'a','b','c');
}
static void hanio(int n ,char from, char tmp, char to){
if(n <= 0) return;
hanio(n-1,from,to,tmp);
sout("将" + n + "从" + from + "移动到" + to);
hanio(n-1,tmp,from,to);
}
动态规划
套路:1. 声明dp数组 2. 计算 dp[0] dp[1] 3. 分析dp[n] 表达式
打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 1:
输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
class Solution {
public int rob(int[] nums) {
int len = nums.length;
if(len == 0) return 0;
int[] dp = new int[len + 1];
dp[0] = 0;
dp[1] = nums[0];
for(int i = 2; i <= len;i++){
dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i-1]);
}
return dp[len];
}
}
分治算法
套路:1. 考虑极端情况(比如只有1个数时) 2. 递归左数组和右数组,分别求值 3. 比较左右数组的返回值
求众数
给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋
的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。
class Solution {
private int countInRange(int[] nums, int num, int lo, int hi) {
int count = 0;
for (int i = lo; i <= hi; i++) {
if (nums[i] == num) {
count++;
}
}
return count;
}
private int majorityElem(int[] nums, int lo, int hi) {
//只有两个数 还相等 那么一定是众数
if (lo == hi) {
return nums[lo];
}
//分治
int mid = (hi-lo)/2 + lo;
int left = majorityElem(nums, lo, mid);
int right = majorityElem(nums, mid+1, hi);
//左右众数相等
if (left == right) {
return left;
}
//计算左众数和右众数的次数
int leftCount = countInRange(nums, left, lo, hi);
int rightCount = countInRange(nums, right, lo, hi);
return leftCount > rightCount ? left : right;
}
public int majorityElement(int[] nums) {
return majorityElem(nums, 0, nums.length-1);
}
}
回溯大法
八皇后
public class EightQueen{
public static int[][] arry=new int[8][8];//棋盘,放皇后
public static int map=0;//存储方案结果数量
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("八皇后问题");
findQueen(0);
System.out.println("八皇后问题共有:"+map+"种可能");
}
public static void findQueen(int i){//寻找皇后节点
if(i>7){//八皇后的解
map++;
// print();//打印八皇后的解
return;
}
for(int m=0;m<8;m++){//深度回溯,递归算法
if(check(i,m)){//检查皇后摆放是否合适
arry[i][m]=1;
findQueen(i+1);
arry[i][m]=0;//清零,以免回溯的时候出现脏数据
}
}
}
public static boolean check(int k,int j){//判断节点是否合适
for(int i=0;i<8;i++){//检查行列冲突
if(arry[i][j]==1){
return false;
}
}
for(int i=k-1,m=j-1; i>=0 && m>=0; i--,m--){//检查左对角线
if(arry[i][m]==1){
return false;
}
}
for(int i=k-1,m=j+1; i>=0 && m<=7; i--,m++){//检查右对角线
if(arry[i][m]==1){
return false;
}
}
return true;
}
public static void print(){//打印结果
System.out.print("方案"+map+":"+"\n");
for(int i=0;i<8;i++){
for(int m=0;m<8;m++){
if(arry[i][m]==1){
//System.out.print("皇后"+(i+1)+"在第"+i+"行,第"+m+"列\t");
System.out.print("o ");
}
else{
System.out.print("+ ");
}
}
System.out.println();
}
System.out.println();
}
}
查找所有回文串
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
class Solution {
List<List<String>> res = new ArrayList();
public List<List<String>> partition(String s) {
if(s.length() == 0) return null;
List<String> sub = new ArrayList();
findHuiWen(sub,s,0);
return res;
}
public void findHuiWen(List<String> sub,String s,int i){
if(i == s.length()){
res.add(sub);
}
for(int j = i + 1; j <= s.length(); j++){
String tmp = s.substring(i,j);
if(isHuiWen(tmp)){
sub.add(tmp);
findHuiWen(new ArrayList<String>(sub),s,j);
sub.remove(sub.size()-1);
}
}
}
public boolean isHuiWen(String s){
return new StringBuffer(s).reverse().toString().equals(s);
}
}
最大堆
不用排序找到第K大的数