数据规模的概念
空间复杂度:(递归占据)
常见的复杂度分析
选择排序O(n^2)
二分查找O(logn)
归并算法O(nlogn)
递归算法的复杂度分析
int binarySearch(int arr[],int l,int r,int target){
if(l>r)
return -1;
int mid=l+(r-l)/2;
if(arr[mid]==target){
return mid;
}else if(arr[mid]>target){
return binarySearch(arr,l,mid-1,target);
}else{
return binarySearch(arr,mid+1,r,target);
}
}
8个元素
均摊复杂度分析
动态数组:
template<typename T>
class MyVector{
private:
T* data;
int capacity;
int size;
//O(n)
void resize(int newCapacity){
assert(newCapacity>=size)
T *newData=new T[newCapacity];
for(int i=0;i<size;i++){
newData[i]=data[i];
}
delete[] data;
data=newData;
capacity=newCapacity;
}
public:
MyVector(){
size=0
}
~MyVector(){
delete[] data;
}
//Average:O(1)
void push_back(T e){
//assert(size<capacity);
if(size==capacity){
resize(2*capacity);
}
data[size++]=e;
}
//O(1)
T pop_back(){
assert(size>0);
T ret=data[size-1];
size--;
if(size==capacity/4){
resize(capacity/2);
}
return ret;
}
};
二分查找法:(维持循环不变量)
template<typename T>
int binarySearch(T arr[],int n,T target){
int l=0,r=n-1;//在[l...r]的范围里寻找target
while(l<=r){
int mid=(l+r)/2;
if(arr[mid]==target){
return mid;
}
if(target>arr[mid]){
l=mid+1;
}else{//target<arr[mid]
r=mid-1;
}
}
return -1;
}
计数排序:Leetcode75
public class merge_88 {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int l=0,r=0;
int[] sorted = new int[m + n];
int cur=0;
while(l<m||r<n){
if(l==m){
sorted[cur++]=nums2[r++];
}else if(r==n){
sorted[cur++]=nums1[l++];
}else if(nums1[l]<=nums2[r]){
sorted[cur++]=nums1[l++];
}else{
sorted[cur++]=nums2[r++];
}
}
for (int i = 0; i < m + n; ++i) {
nums1[i] = sorted[i];
}
}
}
public class findKthLargest_215 {
private final static Random random = new Random(System.currentTimeMillis());
public int findKthLargest(int[] nums, int k) {
// 第 1 大的数,下标是 len - 1;
// 第 2 大的数,下标是 len - 2;
// ...
// 第 k 大的数,下标是 len - k;
int len = nums.length;
int target = len - k;
int left = 0;
int right = len - 1;
while (true) {
int pivotIndex = partition(nums, left, right);
if (pivotIndex == target) {
return nums[pivotIndex];
} else if (pivotIndex < target) {
left = pivotIndex + 1;
} else {
// pivotIndex > target
right = pivotIndex - 1;
}
}
}
private int partition(int[] nums, int left, int right) {
int randomIndex = left + random.nextInt(right - left + 1);
swap(nums, left, randomIndex);
// all in nums[left + 1..le) <= pivot;
// all in nums(ge..right] >= pivot;
int pivot = nums[left];
int le = left + 1;
int ge = right;
while (true) {
while (le <= ge && nums[le] <= pivot) {
le++;
}
while (le <= ge && nums[ge] >= pivot) {
ge--;
}
if (le >= ge) {
break;
}
swap (nums, le, ge);
le++;
ge--;
}
swap(nums, left, ge);
return ge;
}
private void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
}
对撞指针 :
public int[] twoSum(int[] numbers, int target) {
int l=0,r=numbers.length-1;
while(l<r){
if(numbers[l]+numbers[r]==target){
int[] res={l+1,r+1};
return res;
}else if(numbers[l]+numbers[r]<target){
l++;
}else{
r--;
}
}
throw new IllegalStateException("The input has no solution");
}
public class isPalindrome_125 {
//空字符串如何看
//字符的定义
//大小写问题
}
public class reverseString_344 {
public void reverseString(char[] s) {
}
}
public class reverseVowels_345 {
public String reverseVowels(String s) {
}
}
public class maxArea_11 {
public int maxArea(int[] height) {
}
}
双索引技术:滑动窗口
public static int minSubArrayLen(int target, int[] nums) {
//什么叫子数组
//没有解怎么办
//多组解怎么办
int l=0,r=-1;//nums[l,r]为我们的滑动窗口
int sum=0;
int res=nums.length+1;
while(l<nums.length){
if(r+1<nums.length && sum<target){
sum+=nums[++r];
}else{
sum-=nums[l++];
}
if(sum>=target){
res=Math.min(res,r-l+1);
}
}
if(res==nums.length+1){
return 0;
}
return res;
}
public int lengthOfLongestSubstring(String s) {
int[] freq=new int[256];
int l=0,r=-1;
int res=0;
while(l<s.length()){
if(r+1<s.length()&&freq[s.charAt(r+1)]==0){
freq[s.charAt(++r)]++;
}else{
freq[s.charAt(l++)]--;
}
res=Math.max(res,r-l+1);
}
return res;
}
//字符集范围?英文小写字母
//返回的解的顺序?任意
//若没有解?返回“”
//若有多个解?保证只有一个解
两类查找问题
//时间复杂度:O(nlogn)
//空间复杂度:O(n)
public int[] intersect(int[] nums1, int[] nums2) {
TreeMap<Integer,Integer> record=new TreeMap<Integer,Integer>();
//O(nlogn)
for(int num:nums1){
if(!record.containsKey(num)){
record.put(num,1);
}else{
record.put(num,record.get(num)+1);
}
}
ArrayList<Integer> result=new ArrayList<Integer>();
//O(nlogn)
for(int num:nums2){
if(record.containsKey(num)&&record.get(num)>0){
result.add(num);
record.put(num,record.get(num)-1);
}
}
int[] res=new int[result.size()];
int index=0;
for(Integer num:result){
res[index++]=num;
}
return res;
}
哈希表失去了数据的顺序性
public boolean isAnagram(String s, String t) {
if (s.length()!=t.length()){
return false;
}
int[] table=new int[26];
for (int i=0;i<s.length();i++){
table[s.charAt(i)-'a']++;
}
for (int i=0;i<t.length();i++){
if (--table[t.charAt(i)-'a']<0){
return false;
}
}
return true;
}
public boolean isHappy(int n) {
Set<Integer> seen = new HashSet<>();
while(n!=1 && !seen.contains(n)){
seen.add(n);
int sum=0;
while(n>0){
int d = n % 10;
n = n / 10;
sum += d * d;
}
n=sum;
}
return n==1;
}
查找表:
public int[] twoSum(int[] nums, int target) {
int len=nums.length;
Map<Integer,Integer> hash=new HashMap<Integer,Integer>(len-1);
for(int i=0;i<len;i++){
if(hash.containsKey(target-nums[i])){
return new int[]{hash.get(target - nums[i]), i};
}
hash.put(nums[i],i);
}
return new int[0];
}
public List<List<Integer>> threeSum(int[] nums) {
int n=nums.length;
List<List<Integer>> res=new ArrayList();
if (nums==null||nums.length<3){
return res;
}
Arrays.sort(nums);
for (int i=0;i<n;i++){
if (nums[i]>0){
return res;
}
if(i>0 && nums[i]==nums[i-1]){
continue;
}
int l=i+1;
int r=n-1;
while (l<r){
if (nums[l]+nums[r]+nums[i]==0){
res.add(new ArrayList(Arrays.asList(nums[l],nums[r],nums[i])));
while (l<r && nums[l]==nums[l+1]){
l++;
}
while (l<r && nums[r]==nums[r-1]){
r--;
}
l++;
r--;
}else if (nums[l]+nums[r]+nums[i]<0){
l++;
}else {
r--;
}
}
}
return res;
}
public int numberOfBoomerangs(int[][] points) {
int res=0;
for (int i=0;i<points.length;i++) {
HashMap<Integer,Integer> record=new HashMap<>();
for (int j=0;j<points.length;j++) {
if (j!=i){
int dis=dis(points[i],points[j]);
if (record.containsKey(dis)){
record.put(dis,record.get(dis)+1);
}else {
record.put(dis,1);
}
}
}
for (Integer dis:record.keySet()){
res += record.get(dis) * (record.get(dis) - 1);
}
}
return res;
}
private Integer dis(int[] pa, int[] pb) {
return (pa[0] - pb[0]) * (pa[0] - pb[0]) +
(pa[1] - pb[1]) * (pa[1] - pb[1]);
}
滑动窗口+查找表 :
在链表中穿针引线
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre=null;
ListNode cur=head;
while (cur!=null){
ListNode next=cur.next;
cur.next=pre;
pre=cur;
cur=next;
}
return pre;
}
}
public ListNode deleteDuplicates(ListNode head) {
if (head==null){
return null ;
}
ListNode temp=head;
while (temp.next!=null){
if (temp.val==temp.next.val){
temp.next=temp.next.next;
}else {
temp=temp.next;
}
}
return head;
}
public ListNode oddEvenList(ListNode head) {
if (head==null){
return head;
}
ListNode evenHead=head.next;
ListNode odd=head,even=evenHead;
while (even!=null && even.next!=null){
odd.next=even.next;
odd=odd.next;
even.next=odd.next;
even=even.next;
}
odd.next=evenHead;
return head;
}
设立链表的虚拟头节点:
public ListNode removeElements(ListNode head, int val) {
// 创建虚拟头结点
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode cur = dummyHead;
while(cur.next != null){
if(cur.next.val == val ){
ListNode delNode = cur.next;
cur.next = delNode.next;
}
else
cur = cur.next;
}
return dummyHead.next;
}
数组中经典算法运用在链表
不仅仅是穿针引线
public void deleteNode(ListNode node) {
if(node==null){
return;
}
if(node.next==null){
node=null;
}
node.val = node.next.val;
node.next = node.next.next;
}
双指针技术
public ListNode removeNthFromEnd(ListNode head, int n) {
assert (n>=0);
ListNode dummyHead=new ListNode(0);
dummyHead.next=head;
ListNode p=dummyHead;
ListNode q=dummyHead;
for (int i=0;i<n+1;i++){
assert (q!=null);
q=q.next;
}
while (q!=null){
p=p.next;
q=q.next;
}
p.next=p.next.next;
return dummyHead.next;
}
栈
public String simplifyPath(String path) {
String[] names=path.split("/");
Deque<String> stack=new ArrayDeque<>();
for (String name : names){
if ("..".equals(name)){
if (!stack.isEmpty()){
stack.pollLast();
}
}else if (name.length()>0&&!".".equals(name)){
stack.offerLast(name);
}
}
StringBuffer ans=new StringBuffer();
if (stack.isEmpty()){
ans.append("/");
}else {
while (!stack.isEmpty()){
ans.append("/");
ans.append(stack.pollFirst());
}
}
return ans.toString();
}
栈和递归的紧密联系
List<Integer> res=new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
if (root!=null){
res.add(root.val);
preorderTraversal(root.left);
preorderTraversal(root.right);
}
return res;
}
队列
队列和树
public static List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> levelOrder = new LinkedList<List<Integer>>();
if (root == null) {
return levelOrder;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
while (!queue.isEmpty()) {
List<Integer> level=new ArrayList<>();
int size = queue.size();
for (int i=0;i< size;i++){
TreeNode node = queue.poll();
level.add(node.val) ;
if (node.left != null) {
queue.offer(node.left);
}
if (node.right!=null){
queue.offer(node.right);
}
}
levelOrder.add(0,level);
}
return levelOrder;
}
广度优先遍历BFS和图的最短路径
优先队列
寻找最大值/最小值出队,堆的数据结构实现
堆的底层实现,白板编程
public static void main(String[] args) {
// 默认的PriorityQueue, 底层是最小堆
PriorityQueue<Integer> pq = new PriorityQueue<Integer>();
for(int i = 0 ; i < 10 ; i ++){
int num = (int)(Math.random() * 100);
pq.add(num);
System.out.println("insert " + num + " in priority queue.");
}
while (!pq.isEmpty())
System.out.print(pq.poll() + " ");
System.out.println();
System.out.println();
// 使用lambda表达式,创建底层是最大堆的PriorityQueue
PriorityQueue<Integer> pq2 = new PriorityQueue<Integer>(10, (a, b) -> b - a);
for(int i = 0 ; i < 10 ; i ++){
int num = (int)(Math.random() * 100);
pq2.add(num);
System.out.println("insert " + num + " in priority queue.");
}
while (!pq2.isEmpty())
System.out.print(pq2.poll() + " ");
System.out.println();
System.out.println();
// 使用自定义的Comparator,创建个性化的PriorityQueue
// 注意:也可以使用lambda表达式。在这里只是为了演示PriorityQueue的不同用法
// 同理,上一个例子也可以使用自定义的Comparator的方式完成
class myCmp implements Comparator<Integer>{
@Override
public int compare(Integer a, Integer b){
if(a%10 != b%10)
return a%10 - b%10;
return a - b;
}
}
PriorityQueue<Integer> pq3 = new PriorityQueue<Integer>(10, new myCmp());
for(int i = 0 ; i < 10 ; i ++){
int num = (int)(Math.random() * 100);
pq3.add(num);
System.out.println("insert " + num + " in priority queue.");
}
while (!pq3.isEmpty())
System.out.print(pq3.poll() + " ");
System.out.println();
System.out.println();
}
二叉树和递归
堆使用完全二叉树
完全二叉树、满二叉树:所有层的节点数达到最大
注意递归的终止条件
如何使用递归的返回值
public int pathSum(TreeNode root, int targetSum) {
if (root==null){
return 0;
}
int res=findPath(root,(long)targetSum);
res+=pathSum(root.left,targetSum);
res+=pathSum(root.right,targetSum);
return res;
}
//以node为根节点得二叉树中,寻找包含node的路径,和为sum
private int findPath(TreeNode node, Long num) {
if (node==null){
return 0;
}
int res=0;
if (node.val==num){
res+=1;
}
res+=findPath(node.left,num-node.val);
res+=findPath(node.right,num-node.val);
return res;
}
二分搜索树中的问题
O(logn)
map、set是由平衡二叉搜索树实现的。
lca问题(动态规划)
递归与回溯
树形问题
class Solution {
private String letterMap[] = {
" ", //0
"", //1
"abc", //2
"def", //3
"ghi", //4
"jkl", //5
"mno", //6
"pqrs", //7
"tuv", //8
"wxyz" //9
};
private ArrayList<String> res=new ArrayList<String>();;
public List<String> letterCombinations(String digits) {
String path="";
if(digits.equals(""))
return res;
findCombination(digits,0,path);
return res;
}
private void findCombination(String digits, int index, String path) {
if (index==digits.length()){
//保存s
res.add(path);
return;
}
char c=digits.charAt(index);
String letters=letterMap[c-'0'];
for (int i=0;i<letters.length();i++){
findCombination(digits,index+1,path+letters.charAt(i));
}
}
}
回溯法是暴力解法的一个主要实现手段
回溯算法的应用
剪枝
回溯法的剪枝
二维平面上使用回溯法
floodfill算法,一类经典问题(深度优先遍历)
N皇后有很多优化思路
动态规划
斐波那契数列
将原问题拆解成若干子问题,同时保存子问题的答案,使得每个子问题只求解一次,最终获得原问题的答案。
第一个动态规划问题: