001 基礎
- 多塊代碼合在一塊兒,只看最高複雜度的運算
- 時間複雜度
- 常數次數,1,2,3---,時間複雜度都是O(1)
- n,2n,3n---,常數*n次,時間複雜度都是O(n)
- 常見遞歸算法時間複雜度
- 二分查找 時間複雜度是 O(logn)
- 二叉樹遍歷 時間複雜度是 O(n)
- 排序查找 時間複雜度是 O(n)
- 快排,歸併排序 時間複雜度是 O(nlogn)
002 數組及鏈表
數組
- 數組是內存裏連續的一段存儲區域.經過數組下標能夠隨機的訪問任意一個元素.
- 訪問任意數組元素的時間複雜度是O(1)
- 爲了保證數組元素在內存中的連續性,插入和刪除數組元素,時間複雜度是O(n)
鏈表
- 單鏈表
- 雙鏈表
- 插入和刪除的時間複雜度是O(1)
- 查找的時間複雜度是O(n),由於必須從鏈表頭部遍歷查找
003 幾道題
反轉單鏈表
Assume that we have linked list 1 > 2 > 3 > 0, we would like to change it to 0 > 1 > 2 > 3.
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode curr = head.next;
head.next = null;
while(curr != null){
ListNode nextTemp = curr.next;
curr.next = head;
head = curr;
curr = nextTemp;
}
return head;
}
}
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
官方答案分析:
反轉最終結果是原始第一節點的next=null;而後原始第二節點的next是原始第一節點----
想象原始第一節點左邊1個虛擬的節點prev=null.
這樣反轉,則原始第一節點的next變爲prev,原始第二節點的next變爲原始第一節點----
複製代碼
兩兩交換單鏈表中的節點/Swap Nodes in Pairs
Given a linked list, swap every two adjacent nodes and return its head.
Given 1->2->3->4, you should return the list as 2->1->4->3.
class Solution {
public ListNode swapPairs(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode result = head.next;
ListNode curr = head;
ListNode prev = null;
while(curr != null && curr.next != null){
ListNode currTemp = curr.next.next;
if(prev != null){
prev.next = curr.next;
}
curr.next.next = curr;
curr.next = currTemp;
prev = curr;
curr = currTemp;
}
return result;
}
}
複製代碼
判斷鏈表是否有環/Linked List Cycle
Given a linked list, determine if it has a cycle in it.
To represent a cycle in the given linked list, we use an integer pos which represents the position (0-indexed) in the linked list where tail connects to. If pos is -1, then there is no cycle in the linked list. public class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode> set = new HashSet<ListNode>();
while(head != null){
if(set.contains(head)){
return true;
}
set.add(head);
head = head.next;
}
return false;
}
}
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode curr = head;
Map<ListNode,Boolean> map = new HashMap<ListNode,Boolean>();
while(curr != null){
if(map.get(curr) != null){
return true;
}
map.put(curr,true);
curr = curr.next;
}
return false;
}
}
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null || head.next == null){
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while(slow != fast){
if(fast == null || fast.next == null){
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
}
複製代碼
Linked List Cycle II
Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
To represent a cycle in the given linked list, we use an integer pos which represents the position (0-indexed) in the linked list where tail connects to. If pos is -1, then there is no cycle in the linked list. Note: Do not modify the linked list. public class Solution {
public ListNode detectCycle(ListNode head) {
Set<ListNode> set = new HashSet<ListNode>();
while(head != null){
if(!set.add(head)){
return head;
}
head = head.next;
}
return null;
}
}
複製代碼
Reverse Nodes in k-Group
Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.
k is a positive integer and is less than or equal to the length of the linked list.
If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.
Example:
Given this linked list: 1->2->3->4->5
For k = 2, you should return: 2->1->4->3->5
For k = 3, you should return: 3->2->1->4->5
Note:
Only constant extra memory is allowed.
You may not alter the values in the list's nodes, only nodes itself may be changed.
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isGroup(ListNode item, int k){
for(int i=0;i<k;i++){
if(item == null) return false;
item = item.next;
}
return true;
}
public ListNode[] reverseCurrGroup(ListNode prev,ListNode group,int k){
ListNode curr = group;
ListNode first = null;
ListNode last = null;
for(int i=0;i<k;i++){
ListNode next = curr.next;
curr.next = first;
first = curr;
curr = next;
}
if(prev != null){
prev.next = first;
}
return new ListNode[]{first,group,curr};
}
public ListNode reverseKGroup(ListNode head, int k) {
if(isGroup(head,k)){
ListNode[] nodes = reverseCurrGroup(null,head,k);
ListNode result = nodes[0];
ListNode prev = nodes[1];
ListNode group = nodes[2];
while(isGroup(group,k)){
nodes = reverseCurrGroup(prev,group,k);
prev = nodes[1];
group = nodes[2];
}
if(prev != null){
prev.next = group;
}
return result;
}else{
return head;
}
}
}
複製代碼
004 棧和隊列
- 棧是先進後出
- 隊列是先進先出
- 優先隊列 PriorityQueue
- 正常入,按照優先級出
- 優先隊列實現機制:瞭解便可
- 用堆(二叉堆 Binary heap,多項式堆 Binomial heap,斐波那契堆 Fibonacci heap)來實現
- 二叉搜索樹 Binary Search Tree
- 堆自己的實現有不少
- 堆尋找最高優先級/最大最小元素的效率都比較高,衡量一個堆的性能要看 插入,刪除,合併操做的性能
- 二叉堆維護效率不好
- 在java/python中實際使用的堆是斐波拉契堆 或者 使用二叉樹來實現
用棧實現隊列/Implement the following operations of a queue using stacks.
push(x) -- Push element x to the back of queue.
pop() -- Removes the element from in front of queue.
peek() -- Get the front element.
empty() -- Return whether the queue is empty.
class MyQueue {
Stack<Integer> s1 = new Stack<Integer>();
Stack<Integer> s2 = new Stack<Integer>();
public MyQueue() {
}
public void push(int x) {
s1.push(x);
}
public int pop() {
if(s2.empty()){
while(!s1.empty()){
s2.push(s1.pop());
}
}
return s2.pop();
}
public int peek() {
if(s2.empty()){
while(!s1.empty()){
s2.push(s1.pop());
}
}
return s2.peek();
}
public boolean empty() {
return s1.empty() && s2.empty();
}
}
複製代碼
用隊列實現棧/Implement the following operations of a stack using queues.
push(x) -- Push element x onto stack.
pop() -- Removes the element on top of the stack.
top() -- Get the top element.
empty() -- Return whether the stack is empty.
class MyStack {
LinkedList<Integer> q = new LinkedList<Integer>();
public MyStack() {
}
public void push(int x) {
int size = q.size();
q.add(x);
while(size-- > 0){
q.add(q.poll());
}
}
public int pop() {
return q.poll();
}
public int top() {
return q.peek();
}
public boolean empty() {
return q.size() == 0;
}
}
複製代碼
005
流式數據中尋找第K大的元素
- 對K個元素進行排序,最佳排序方法是快排,時間複雜度是O(KlogK)
- 優先隊列對元素進行自動排序,時間複雜度是 O(logk)
- 在N個元素中尋找第K大的元素
- 方法1.保存K個元素,而後每一個新元素和K箇中的最小值比較,替換並從新排序
- 若是先保存K個元素,而後進行排序.時間複雜度是 O(klogk)
- 之後每一個新元素都和以前保存的K個元素進行比較,大於其中最小的元素min,就將min移除,而後將新元素加入並排序.也就是每一次都須要從新排序.
- 整體時間複雜度是 O(Nklogk)
- 方法2.使用優先隊列
- 在java中,優先隊列是使用斐波拉契堆實現
- 使用優先隊列,保持優先隊列中元素數量爲K
- 每一個新元素和優先隊列中最小的進行比較
- 比堆頂元素小,則略過
- 比堆頂元素大,咋刪除堆頂元素,將新元素加入優先隊列,優先隊列會自動進行排序
- 最後返回堆頂元素便可
- 優先隊列自動排序時間複雜度是O(logk)
- 整體時間複雜度是 O(Nlogk)
- 因而可知,使用優先隊列時間複雜度低,比使用快排整整下降了k倍.
Kth Largest Element in a Stream
Design a class to find the kth largest element in a stream. Note that it is the kth largest element in the sorted order, not the kth distinct element. Your KthLargest class will have a constructor which accepts an integer k and an integer array nums, which contains initial elements from the stream. For each call to the method KthLargest.add, return the element representing the kth largest element in the stream. class KthLargest {
PriorityQueue<Integer> queue;
int k;
public KthLargest(int k, int[] nums) {
this.k = k;
this.queue = new PriorityQueue<Integer>(k);
for(int val : nums){
add(val);
}
}
public int add(int val) {
if(queue.size() < k){
queue.offer(val);
}else if(queue.peek() < val){
queue.poll();
queue.offer(val);
}
return queue.peek();
}
}
複製代碼