学习总结

学习内容

课程内容

  • 第三课

    • 数组
    • 链表
    • 跳表
  • 第四课
    • 队列

知识点总结

数组

数组用一块连续的内存空间,来存储相同类型的一组数据。
支持随机访问,时间复杂度 O(1)
插入、删除操作比较低效,为了满足连续空间需要进行数据的搬移,平均情况时间复杂度为O(n)

链表
链表内存空间可以不连续
链表类型有:单链表、双向链表、循环链表、双向循环链表等
更适合插入、删除操作频繁的场景,时间复杂度 O(1)
但是访问时需要遍历链表 ,平均情况时间复杂度为O(n)
某些情况下双向链表的访问比单链表更高效,如指定访问某个节点前面的节点
为了提高访问效率,用空间换时间的设计思路出现跳表

跳表
通过空间换时间,构建多级索引来提高查询的效率,实现了基于链表的“二分查找”
是一种动态数据结构,支持快速的插入、删除、查找操作,时间复杂度为O(nlogn)


栈是一种受限的数据结构,只支持入栈和出栈操作,其入栈、出栈时间复杂度为O(1)。特点是先入后出。

队列
特点是先入先出,常见 有 优先队列,双端队列

关于迭代和递归的一些总结

个人理解如果不同意见欢迎探讨,如果错误欢迎指正
迭代和递归都是要找到其重复处理单元,不同的地方是
迭代:思路是从前向后,大多数情况下使用哨兵节点能简化处理流程
递归:思路是从后向前,仅关注当前层和下一层,重复操作就不要向下考虑太多不然容易晕

递归

先找重复处理单元,再找递归终止条件,解决了这两个问题就很好处理了
就链表处理,我将递归相关题目归纳为以下几点:

  • 先找重复处理单元
  • 递归终止条件
  • 递归前处理(递归到下一层前处理):
  • 递归(递归到下一层):
  • 递归后处理(下一层递归返回后处理)

具体可以参看下面的例子

迭代

同样先找重复处理单元,再找迭代终止条件
就链表处理,我将迭代相关题目归纳为以下几点:

  • 先找重复处理单元
  • 迭代终止条件
  • 迭代前处理
  • 迭代处理
  • 迭代后处理

具体可以参看下面的例子

例子

例子 24. 两两交换链表中的节点

递归实现

#@author:leacoder
#@des: 递归实现  两两交换链表中的节点'''重复处理单元:
两两交换
链表可以分为   待处理 + 当前层正处理(两两交换的两个节点) + 下层递归已处理下一层递归返回已处理头结点
当前层就知道: 需要交换的 a、b 两节点 以及 后续已处理头节点递归终止条件:
head.next 为 None
可以将 head 为 None 的特殊情况一起做判断
if not head or not head.next:return head递归前处理(递归到下一层前处理):
这里两两交换链表中的节点 , 下一次递归从 下 两个的结点开始  题 25. K 个一组翻转链表 则另一种处理
可以记录下 当前的 next
next = head.next递归(递归到下一层):
下两位,使用递归前处理的 next
p = swapPairs(next.next)   题 25. K 个一组翻转链表 则 会有不同 递归后处理(下层递归返回后处理):1->2->3->4
2->1->4->3'''
class Solution:def swapPairs(self, head: ListNode) -> ListNode:# 递归终止条件:if not head or not head.next:return head# 递归前处理(递归到下一层前处理):# 当前层 需交换节点 为  head (a) 、head.next (b)next = head.next# 递归(递归到下一层):resultmp = self.swapPairs(next.next) # 下移两位  返回值为两两交换后  两个节点中的前一个节点# 递归后处理(下层递归返回后处理):head.next = resultmp  # 两两交换next.next = head  # 两两交换''' 语法糖head.next, next.next = self.swapPairs(next.next), head'''return next   # 返回 两两交换后  两个节点中的前一个节点

迭代实现

#@author:leacoder
#@des: 迭代实现  两两交换链表中的节点'''
利用哨兵简化操作1->2->3->42->1->4->3重复处理单元:
两两交换
链表可以分为   已处理 + 正处理(两两交换的两个节点) + 后续迭代待处理迭代终止条件:
两两交换,迭代处理这两个节点,那么后续节点不足两个时跳出迭代
prev.next ,prev.next.next 为 None 迭代前处理:
利用哨兵简化操作,添加哨兵节点 prev
最终结果  以 2->1->4->3 为例 其头节点为 2
由于两两交换 prev 也会跟随下移,先 用 retult = prev 记录prev下移前的值用于最终结果返回迭代处理:1->2->3->4 为例
将需要翻转操作的两个节点记录(要断开连接,先记录)
a = prev.next   (1)
b = prev.next.next  (2)
交换两节点
prev 节点下移,迭代开始新一轮处理 (由于两两交换,跳过两个节点)迭代后处理:
无特殊处理'''class Solution:def swapPairs(self, head: ListNode) -> ListNode:# 迭代前处理:prev = ListNode(None) # 哨兵prev.next = headretult = prev # 记录  第一次被操作前的prev 用于返回结果# 迭代终止条件 prev.next ,prev.next.next 为 None while prev.next and prev.next.next: # 迭代处理:# 以 1 2 反转为例,prev为哨兵a = prev.next   # 由于 要操作 prev.next (1) 和 prev.next.next (2) 先记录下来b = prev.next.next  # 记录 要交换的两节点prev.next = b  # (2)a.next = b.next # (3)b.next = a  # (1)prev = a # (1) # 向下移动2位''' 使用语法糖prev.next,a.next,b.next= b,b.next,a # 2个节点交换,注意哨兵的next改变了prev = a # 向下移动2位'''# 迭代后处理 无return retult.next

例子 25. K 个一组翻转链表

递归实现


#@author:leacoder
#@des: 递归实现   k个一组翻转链表'''重复处理单元:
k个一组翻转
链表可以分为 : 待处理 + 当前层正处理(k个一组链表) + 下层递归已处理下一层递归返回已处理头结点
当前层就知道: 需要翻转k个一组链表的链表头、链表尾
链表头 head 参数
链表尾 遍历 k 个节点递归终止条件:
后续迭代待处理剩余节点不足 k 个递归前处理(递归到下一层前处理):
k个一组翻转链表,下一次递归从 下 k 个节点开始
记录 翻转前的 链表头 链表尾递归(递归到下一层):
下k位,使用递归前处理的 链表尾.next递归后处理(下层递归返回后处理):
将下一层递归返回结果加入到原链表
K 个一组链表翻转,'''
class Solution:def reverseKGroup(self, head: ListNode, k: int) -> ListNode:# 用于记录 翻转前的 链表头 链表尾end = headcount = 0# 递归终止条件while end and count!= k:end = end.nextcount += 1if count == k:  # 递归终止条件# 递归前处理(递归到下一层前处理) # 记录 翻转前的 链表头 head 链表尾 end 在前面已做# 递归(递归到下一层):resultmp = self.reverseKGroup(end,k) # 下层 已处理头结点 # 递归后处理(下层递归返回后处理)while count:    # K 个一组链表翻转,tmp = head.nexthead.next = resultmp  # 将下一层递归返回结果加入到原链表resultmp = headhead = tmpcount -= 1head = resultmp # 翻转后链表头return head

迭代实现

#@author:leacoder
#@des: 迭代实现   k个一组翻转链表'''
利用哨兵简化操作
prev = ListNode(None) # 哨兵
prev.next = head
prev 每次迭代下移(下移位数为k)重复处理单元:
k个一组翻转
链表可以分为 :已处理 + 正处理(k个一组链表) + 后续迭代待处理
正处理(k个一组链表)需要知道 链表头和链表尾
链表头 可以通过 prev 的 next 获取
链表尾 需要遍历k个节点获取迭代终止条件:
后续迭代待处理剩余节点不足 k 个迭代前处理:
result 记录 prev 下移前的值 用于 结果返回
end 参数 记录尾节点
prev、end 每次迭代指向同一节点迭代处理:
遍历k个节点获取 链表尾
构建 k个一组链表 并进行翻转 reverse()
reverse() 翻转函数返回 翻转后新链表头
将翻转后新链接入原链表
prev 下移 end 下移迭代后处理:
无'''class Solution:def reverseKGroup(self, head: ListNode, k: int) -> ListNode:# 迭代前处理:prev = ListNode(None) # 哨兵prev.next = headresult = prevend = prev  # end 记录 k个一组链表 链表尾  prev.next 记录 k个一组链表 链表头# 迭代终止条件:while(end.next): # 剩余节点不足 k 个for i in range(k): # 遍历k个节点获取 链表尾 并 判断 剩余节点 是否不足 k 个if not end: # 不足 k 个breakend = end.nextif not end: # 不足 k 个break'''start = prev.next   # k个一组链表 链表头next = end.next     # 记录 原 end.next end.next = None     # k个一组链表 链表尾'''# 构建 k个一组的链表start, next, end.next = prev.next, end.next, None# 翻转 k个一组的链表prev.next = self.reverse(start) # 返回 翻转后新链表头 接入原链表start.next = next  # 接入原链表  start 为 翻转后新链表尾prev = startend = startreturn result.nextdef reverse(self, head: ListNode):prev = Nonecurr = headwhile(curr):'''tmp = curr.nextcurr.next = prevprev = currcurr = tmp'''curr.next, prev, curr = prev, curr, curr.nextreturn prev

课后作业 改写Deque的代码

课程中示例代码-Deque

代码

import java.util.LinkedList;
import java.util.Deque;public class DequeExample {public static void main(String[] args) {Deque<String> deque = new LinkedList<String>();deque.push("a");deque.push("b");deque.push("c");System.out.println(deque);String str = deque.peek();System.out.println(str);System.out.println(deque);while (deque.size() > 0) {System.out.println(deque.pop());}System.out.println(deque);}
}

输出:

[c, b, a]
c
[c, b, a]
c
b
a
[]

接口说明

push

public void push​(E e)

Pushes an element onto the stack represented by this list. In other words, inserts the element at the front of this list.
This method is equivalent to addFirst(E).

将元素压入此列表表示的堆栈中。换句话说,将元素插入此列表的前面。

此方法等效于addFirst(E)

pop

public E pop()

Pops an element from the stack represented by this list. In other words, removes and returns the first element of this list.
This method is equivalent to removeFirst().

从此列表表示的堆栈中弹出一个元素。换句话说,删除并返回此列表的第一个元素。
此方法等效于equivalent to removeFirst().

peek

public E peek()

Retrieves, but does not remove, the head (first element) of this list.

检索但不删除此列表的头(第一个元素)。

addFirst 改写 Deque 代码

代码

import java.util.LinkedList;
import java.util.Deque;public class DequeExample {public static void main(String[] args) {Deque<String> deque = new LinkedList<String>();deque.addFirst("a");    // 使用 addFirst 替换 pushdeque.addFirst("b");deque.addFirst("c");System.out.println(deque);String str = deque.peek();System.out.println(str);System.out.println(deque);while (deque.size() > 0) {System.out.println(deque.removeFirst()); // 使用 removeFirst 替换 pop}System.out.println(deque);}
}

输出:

[c, b, a]
c
[c, b, a]
c
b
a
[]

接口说明

addFirst

void addFirst​(E e)

Inserts the specified element at the front of this deque if it is possible to do so immediately without violating capacity restrictions, throwing an IllegalStateException if no space is currently available. When using a capacity-restricted deque, it is generally preferable to use method offerFirst(E).

如果可以在不违反容量限制的情况下立即执行此操作,则将指定的元素插入此双端队列的前面,如果当前没有可用空间,则抛出IllegalStateException。使用容量受限的双端队列时,通常最好使用方法offerFirst(E)。

removeFirst

E removeFirst()

Retrieves and removes the first element of this deque. This method differs from pollFirst only in that it throws an exception if this deque is empty.

检索并删除此双端队列的第一个元素。此方法与pollFirst的不同之处仅在于,如果此双端队列为空,则它将引发异常。

Queue和PriorityQueu源码分析

什么是 Queue

数据结构中的队列,先进先出式的数据结构,所有新元素都插入队列的末尾,移除元素都移除队列的头部。主要注意的时,Java中的Queue是一个接口。

Queue 源码分析

public interface Queue<E> extends Collection<E> {boolean add(E e);   //往队列插入元素,如果出现异常会抛出异常boolean offer(E e); //往队列插入元素,如果出现异常则返回falseE remove(); //移除队列元素,如果出现异常会抛出异常E poll();   //移除队列元素,如果出现异常则返回nullE element();    //获取队列头部元素,如果出现异常会抛出异常E peek();   //获取队列头部元素,如果出现异常则返回null
}

上面六个函数总体上分为两类:安全的会进行容量检查的(add,remove,element),如果队列没有值,则取元素会抛出IlleaglStatementException异常。不安全的不进行容量控制的(offer,poll,peek )。

AbstractQueue 是Queue 的抽象实现类, AbstractQueue 也继承自 AbstractCollection 。AbstractQueue 实现的方法不多,主要就 add、remove、element 三个方法的操作失败抛出了异常。

什么是 PriorityQueue

PriorityQueue 优先级队列, ,不同于普通的遵循FIFO(先进先出)规则的队列,每次都选出优先级最高的元素出队,优先队列里实际是维护了这样的一个堆,通过堆使得每次取出的元素总是最小的(用户可以自定义比较方法,相当于用户设定优先级)。 PriorityQueue 是一个小顶堆,是非线程安全的;PriorityQueue不是有序的,只有堆顶存储着最小的元素;从数据的存储结构看, PriorityQueue 一个数组;从逻辑结构看, PriorityQueue 是一棵平衡二叉树。

什么是 PriorityQueue

PriorityQueue 优先级队列, ,不同于普通的遵循FIFO(先进先出)规则的队列,每次都选出优先级最高的元素出队,优先队列里实际是维护了这样的一个堆,通过堆使得每次取出的元素总是最小的(用户可以自定义比较方法,相当于用户设定优先级)。 PriorityQueue 是一个小顶堆,是非线程安全的;PriorityQueue不是有序的,只有堆顶存储着最小的元素;从数据的存储结构看, PriorityQueue 一个数组;从逻辑结构看, PriorityQueue 是一棵平衡二叉树。

PriorityQueue 源码分析

继承关系

public class PriorityQueue<E> extends AbstractQueue<E>implements java.io.Serializable{// 略}

PriorityQueue 继承了AbstractQueue 类,而AbstractQueue 类实现了Queue接口。

主要属性

// 默认容量
private static final int DEFAULT_INITIAL_CAPACITY = 11;
// 存储元素的数组,Object类型的
transient Object[] queue;
// 元素个数
int size;
// 比较器
private final Comparator<? super E> comparator;
// 修改次数
transient int modCount;

transient Object[] queue
源码注释

Priority queue represented as a balanced binary heap: the two children of queue[n] are queue[2*n+1] and queue[2*(n+1)].  The priority queue is ordered by comparator, or by the elements' natural ordering, if comparator is null: For each node n in the heap and each descendant d of n, n <= d.  The element with the lowest value is in queue[0], assuming the queue is nonempty.

优先级队列是一个平衡的二叉树堆,queue[n]的两个子节点queue[2n+1] and queue[2(n+1)]。 优先级队列 按照 comparator 进行排序或者 按自然顺序排序。 如果 comparator 为null ,堆中每一个节点 n 以及 n的后代d, n<=d。假设队列为非空,则具有最小值的元素位于queue [0]中。

常用构造函数

  • 1、创建一个优先队列对象,默认大小,队列中的元素按照自然顺序排序。
public PriorityQueue() {this(DEFAULT_INITIAL_CAPACITY, null);}
  • 2、创建一个指定大小的优先队列对象,队列中的元素按照自然顺序排序。
public PriorityQueue(int initialCapacity) {this(initialCapacity, null);}
  • 3、创建一个默认大小(11)的优先队列对象,队列中的元素按照指定比较器进行排序。
public PriorityQueue(Comparator<? super E> comparator) {this(DEFAULT_INITIAL_CAPACITY, comparator);}
  • 4、根据指定的大小和比较器来创建一个优先队列对象。
public PriorityQueue(int initialCapacity,Comparator<? super E> comparator) {// Note: This restriction of at least one is not actually needed,// but continues for 1.5 compatibilityif (initialCapacity < 1)throw new IllegalArgumentException();this.queue = new Object[initialCapacity];this.comparator = comparator;}

add(e)/offer(e)方法介绍

add方法
插入一个元素到优先队列中

add方法的源代码如下:

public boolean add(E e) {return offer(e);
}

从源码中可以看出add方法直接调用了offer方法。

offer(e)方法

public boolean offer(E e) {if (e == null)  //检查是否为null, 如果为null 则抛空指针异常throw new NullPointerException();modCount++;int i = size;if (i >= queue.length)  // 检查容量是否足够, 不足进行扩容grow(i + 1);    // 扩容函数siftUp(i, e);   // 在数组的末尾插入新的元素,向上调整,使之保持为二叉堆的特性。size = i + 1;return true;
}

1)首先检查要添加的元素e是否为null,如果为null,则抛空指针异常,如果不为null,则进行2
2)判断数组是否已满,如果已满,则进行扩容,否则将元素加入到数组末尾即可。
由于这个数组表示的是一个“二叉堆”,因此还需要进行相应的调整操作,使得这个数组在添加元素之后依然保持的是二叉堆的特性。

grow(int minCapacity)方法

//增加数组的容量
private void grow(int minCapacity) {int oldCapacity = queue.length;// Double size if small; else grow by 50%//如果比较小,则扩容为原来的2倍,否则只扩容为原来的1.5倍int newCapacity = oldCapacity + ((oldCapacity < 64) ?(oldCapacity + 2) :(oldCapacity >> 1));// overflow-conscious codeif (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);queue = Arrays.copyOf(queue, newCapacity);
}

hugeCapacity(int minCapacity) 方法

private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}

注:
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

public static final int MAX_VALUE = 0x7fffffff;

当 大于 MAX_ARRAY_SIZE 时扩容记性了特殊处理

siftUp(int k, E x)方法

将元素x插入到queue[k]位置上,并进行调整使之具有二叉堆特性

private void siftUp(int k, E x) {if (comparator != null)siftUpUsingComparator(k, x);    // 使用比较器 comparatorelsesiftUpComparable(k, x);
}

siftUpUsingComparator和siftUpComparable源码如下

从 位置 k 向数组起始位置 调整使之具有二叉堆特性,自下而上的堆化,

也就是 二叉堆插入元素思想操作:

可参见 拜托,面试别再问我堆(排序)了!

siftUpComparable(int k, E x)方法

@SuppressWarnings("unchecked")
private void siftUpComparable(int k, E x) {Comparable<? super E> key = (Comparable<? super E>) x;while (k > 0) {int parent = (k - 1) >>> 1; // 找 parent 父节点Object e = queue[parent];if (key.compareTo((E) e) >= 0) // 比较 按 自然顺序 排列break;queue[k] = e;   k = parent; }queue[k] = key;
}

siftUpUsingComparator(int k, E x)方法

@SuppressWarnings("unchecked")
private void siftUpUsingComparator(int k, E x) {while (k > 0) {int parent = (k - 1) >>> 1;Object e = queue[parent];if (comparator.compare(x, (E) e) >= 0)  // 使用比较器 comparatorbreak;queue[k] = e;k = parent;}queue[k] = x;
}

poll()方法介绍

取出优先队列中的第一个元素

public E poll() {if (size == 0)  // 优先队列大小判断return null;int s = --size;modCount++;E result = (E) queue[0]; // 取出第一个元素E x = (E) queue[s]; // 取出最后一个元素queue[s] = null;if (s != 0)siftDown(0, x); return result;
}

poll方法:取出queue[0]元素,然后将queue[size-1]插入到queue[0],然后自上而下堆化来保持二叉堆的特性。

siftDown(int k, E x)方法

//将元素x存储在queue[k],并进行相应的调整
private void siftDown(int k, E x) {if (comparator != null)siftDownUsingComparator(k, x);elsesiftDownComparable(k, x);
}

siftDownComparable(int k, E x)方法

private void siftDownComparable(int k, E x) {Comparable<? super E> key = (Comparable<? super E>)x;int half = size >>> 1;        // loop while a non-leafwhile (k < half) {int child = (k << 1) + 1; // assume left child is leastObject c = queue[child];int right = child + 1;if (right < size &&((Comparable<? super E>) c).compareTo((E) queue[right]) > 0) //两个儿子节点较小的那一个c = queue[child = right];if (key.compareTo((E) c) <= 0)break;queue[k] = c;k = child;}queue[k] = key;
}

siftDownUsingComparator(int k, E x)方法

private void siftDownUsingComparator(int k, E x) {int half = size >>> 1;while (k < half) {int child = (k << 1) + 1;Object c = queue[child];int right = child + 1;if (right < size &&comparator.compare((E) c, (E) queue[right]) > 0)c = queue[child = right];if (comparator.compare(x, (E) c) <= 0)break;queue[k] = c;k = child;}queue[k] = x;
}

remove(Object o) 方法介绍

移除指定元素

public boolean remove(Object o) {int i = indexOf(o);     // 判断位置if (i == -1)    //没有在数组中找到return false;else {removeAt(i);    //进行删除并调整return true;}
}

indexOf(Object o)方法

private int indexOf(Object o) {if (o != null) {for (int i = 0; i < size; i++)  // 数组遍历过程if (o.equals(queue[i]))return i;}return -1;
}

找到对象o在数组中出现的第一个索引

emoveAt(int i)方法

E removeAt(int i) {// assert i >= 0 && i < size;modCount++;int s = --size;if (s == i) // removed last elementqueue[i] = null;else {E moved = (E) queue[s]; // 取出最后一个元素queue[s] = null;siftDown(i, moved); //从i向下调整堆if (queue[i] == moved) {    // 如果发现调整后 moved 还在 i 位置没有下沉,向上调整看是否上浮siftUp(i, moved);if (queue[i] != moved)return moved;}}return null;
}

堆插入元素与删除堆顶元素操作

以下面这个对为例

用数组来存储为:

插入元素

往堆中插入一个元素后,需要继续满足堆的两个特性,即:

(1)堆是一颗完全二叉树;
(2)堆中某个节点的值总是不大于(或不小于)其父节点的值。

把元素插入到最后一层最后一个节点往后一位的位置,也就是在数组的末尾插入新的元素
,插入之后可能不再满足条件(2),这时候我们需要调整,自下而上的堆化。

上面那个堆插入元素2,我们把它放在9后面,这时不满足条件(2)了,我们就需要堆化。(这是一个小顶堆)


从插入元素的过程,我们知道每次与 n/(2x)n/(2^x)n/(2x) 的位置进行比较,所以,插入元素的时间复杂度为 O(logn)O(log n)O(logn)。

删除堆顶元素

删除了堆顶元素后,要使得还满足堆的两个特性。把最后一个元素移到根节点的位置,这时候就满足条件(1),之后就需要堆化了使它满足条件(2)


从删除元素的过程,我们知道把最后一个元素拿到根节点后,每次与 2n2n2n 和 (2n+1)(2n+1)(2n+1) 位置的元素比较,取其小者,所以,删除元素的时间复杂度也为 O(logn)O(log n)O(logn) 。

回到 remove(Object o) 方法 ,不是删除堆顶元而是删除指定元素,源码中会先找Object o在数组中位置 i,再取出最后一个元素从i自上而下堆化,如果最后一个元素没有下沉,会从i自下而上堆化

极客时间 算法训练营 第一周总结相关推荐

  1. 极客时间-算法训练营-第二周

    文章目录 学习笔记 1.哈希表 定义 图示 具体实现是Map和Set HashSet源码分析 HashMap源码分析 时间复杂度 实战题目 [242. 有效的字母异位词](https://leetco ...

  2. 极客时间-算法训练营1.2 实战题目解析:移动零

    一 序 本文属于极客时间-算法训练营 学习笔记系列. 二 刷题步骤 第一遍 五分钟:读题 + 思考 直接看解法:多看几种,比较解法优劣 背诵.默写好的解法 第二遍 马上自己写 --> Leetc ...

  3. 极客时间 算法训练营 毕业总结

    不知不觉8周的算法训练营也接近尾声,这期间训练营对自己的影响有三方面 一方面是收获了刻意练习,终身成长这些可以产生长远影响的思想,这里推荐三本书 卡罗尔·德韦克的<终身成长>.安德斯·艾利 ...

  4. 极客时间算法训练营2019版LeetCode练习题题库

    03课 数组.链表.跳表 两数之和题目 Arrays实战 LeetCode#11 盛最多水的容器 LeetCode#283 移动零 LeetCode#70 爬楼梯 LeetCode#15 三数之和 L ...

  5. 极客大学算法训练营_训练营与大学

    极客大学算法训练营 来自技术招聘公司的数据 对编程训练营进行编程似乎是不可能的. 他们说,与其花四年的大学时间,您可以学习如何成为一个为期三个月的计划的软件工程师. 从表面上看,这听起来更像是特朗普大 ...

  6. 笔记1 第11课 贪心初步 ——柠檬水找零,分发饼干,跳跃游戏,完成所有任务所需最小能量——极客时间算法

    之前收藏了极客时间的算法训练营3期 共21课,计划每一课写博客来记录学习,主要形式为 方法类型1 题1 题解 题2 题解 方法类型2 题1 题解 -- 题目大体来自leetcode 和 acwing ...

  7. 笔记1 第13课 动规2 ——买卖股票最佳时机,打家劫舍,零钱找兑——极客时间算法

    之前收藏了极客时间的算法训练营3期 共21课,计划每一课写博客来记录学习,主要形式为 方法类型1 题1 题解 题2 题解 方法类型2 题1 题解 -- 题目大体来自leetcode 和 acwing ...

  8. 极客时间算法练习题总结

    文章出处:极客时间<数据结构和算法之美>-作者:王争.该系列文章是本人的学习笔记. 在极客时间<数据结构和算法之美>最后,王争老师加餐了7天训练内容,对每一部分需要掌握的数据结 ...

  9. 极客时间 自我提升第一天

    菜鸟又多日与大家未见,甚是想念,在回顾自己的大学生涯中,发现了自己根本没有学好任何东西,所以趁疫情不用归校,好好的查漏补缺,于是规划了3个月学习目标,我会将所看重点全部记录,越学习写的东西自然会慢慢深 ...

最新文章

  1. GPT-3 不够 Open,BigScience 构建开放语言模型,规模小 16 倍
  2. Spark 大数据处理最佳实践
  3. jsp 嵌入java_关于JSP里的Java语句嵌入问题
  4. code vs 1013 求先序排列
  5. [转] 雅虎团队:网站性能优化的35条黄金守则
  6. 抽象类、接口、内部类
  7. 解决lay out无法使用闪退问题,SketchUp 2022 MAC中文 (草图大师) 支持M1intel芯片,支持monterey最新系统
  8. Android 集成google地图
  9. Asp.net mvc5开源项目超级冷笑话
  10. apple tv 开发_如何在Apple TV上禁用Siri和定位服务
  11. K.论演员的自我修养---组合数的运用路径计数
  12. 【8.8gzoj综合】贪|污排名【搜索二叉树】
  13. About 9.18 This Week
  14. 在Linux手机上编译和运行2048游戏学习C++
  15. 3月份GitHub上最热门的JavaScript开源项目(下)
  16. 网页转换成markdown
  17. LintCode-解码方法
  18. python基础教程:教你如何自学python入门到精通
  19. Linux开发环境搭建之bzip2离线安装
  20. Ubuntu下安装Microsoft Teams

热门文章

  1. (ACWing217)绿豆蛙的归宿(数学期望)
  2. 小明加密通道进入_门禁系统跟闸机通道的区分是什么?功能是一样吗
  3. 一个印度人写的VC串口类CSerialCom(有串口基础介绍)
  4. python 入门到实践期末考试常出现的考试内容_测试类——python编程从入门到实践...
  5. Flutter 视频播放器组件封装
  6. Mac OS X: MS Office 2008 Preference Manifests
  7. office2007安装找不到文件问题
  8. Metal 框架之渲染到多个纹理切片
  9. python生僻字如何转码_如何用Python检测生僻字
  10. 马海峰,杨家海,计算机应用,一种非同频远程数据持有检测方法