数据结构总结
- PriorityQueue
import java.util.PriorityQueue;
import java.util.Queue;
class XX{
int a;
String b;
XX(int a,String b){
this.a = a;
this.b = b;
}
}
class Test{
public static void main(String[] args) {
Queue<XX> pq = new PriorityQueue<XX>((x1, x2) ->{return x1.a - x2.a;});
pq.offer(new XX(2,"XX"));
pq.offer(new XX(3,"YY"));
pq.offer(new XX(-1,"ZZ"));
pq.offer(new XX(1,"zzz"));
System.out.println(pq.poll().b);
}
}
x.compareTo(b) ----> -1 means x小于y ; 1 means x大于y
- Deque
import java.util.ArrayDeque;
import java.util.Deque;
class Test{
public static void main(String[] args) {
Deque<String> queue = new ArrayDeque<String>();
queue.offer("aaa");
queue.offer("bbb");
queue.offer("ccc");
queue.push("xxx");
queue.push("yyy");
queue.push("zzz");
System.out.println(queue.peekFirst());
System.out.println(queue.peekLast());
System.out.println(queue.pollFirst());
System.out.println(queue.pollLast());
System.out.println(queue.contains("bbb"));
}
}
时间复杂度
常数阶 对数阶 线性阶 线性对数阶 平方阶 指数阶
O(1) O(log2n) O(n) O(nlog2n) O(n2) O(2n)
Example One:
void aFunc(int n) {
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
printf("Hello World\n");
}
}
}
参考答案:
当 i = 0 时,内循环执行 n 次运算,当 i = 1 时,内循环执行 n - 1 次运算……当 i = n - 1 时,内循环执行 1 次运算。
所以,执行次数 T(n) = n + (n - 1) + (n - 2)……+ 1 = n(n + 1) / 2 = n^2 / 2 + n / 2。
根据上文说的 大O推导法 可以知道,此时时间复杂度为 O(n^2)。
Example Two:
void aFunc(int n) {
for (int i = 2; i < n; i++) {
i *= 2;
printf("%i\n", i);
}
}
参考答案:
假设循环次数为 t,则循环条件满足 2^t < n。
可以得出,执行次数t = log(2)(n),即 T(n) = log(2)(n),可见时间复杂度为 O(log(2)(n)),即 O(log n)。
Example Three:
long aFunc(int n) {
if (n <= 1) {
return 1;
} else {
return aFunc(n - 1) + aFunc(n - 2);
}
}
参考答案:
显然运行次数,T(0) = T(1) = 1,同时 T(n) = T(n - 1) + T(n - 2) + 1,这里的 1 是其中的加法算一次执行。
显然 T(n) = T(n - 1) + T(n - 2) 是一个斐波那契数列,通过归纳证明法可以证明,当 n >= 1 时 T(n) < (5/3)^n,同时当 n > 4 时 T(n) >= (3/2)^n。
所以该方法的时间复杂度可以表示为 O((5/3)^n),简化后为 O(2^n)。
各种算法排序:
排序算法 平均时间复杂度
冒泡排序 O(n2)
选择排序 O(n2)
插入排序 O(n2)
希尔排序 O(n1.5)
快速排序 O(N*logN)
归并排序 O(N*logN)
堆排序 O(N*logN)
基数排序 O(d(n+r))
冒泡排序
import java.util.Arrays;
class ss {
public static void main(String[] args) {
int[] arr = {3,1,2,5,1};
for (int i = 0; i < arr.length - 1; i++)
{
for (int j = arr.length - 1; j > i; j--)
{
if (arr[j] < arr[j - 1])
{
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
}
}
}
Arrays.stream(arr).forEach(System.out::println);
}
}
优化
public static void BubbleSort1(int [] arr){
int temp;//临时变量
boolean flag;//是否交换的标志
for(int i=0; i<arr.length-1; i++){ //表示趟数,一共arr.length-1次。
flag = false;
for(int j=arr.length-1; j>i; j--){
if(arr[j] < arr[j-1]){
temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
flag = true;
}
}
if(!flag) break;
}
}
选择排序
public static void select_sort(int array[],int lenth){
for(int i=0;i<lenth-1;i++){
int minIndex = i;
for(int j=i+1;j<lenth;j++){
if(array[j]<array[minIndex]){
minIndex = j;
}
}
if(minIndex != i){
int temp = array[i];
array[i] = array[minIndex];
array[minIndex] = temp;
}
}
}
插入排序
public static void insert_sort(int array[],int lenth){
int temp;
for(int i=0;i<lenth-1;i++){
for(int j=i+1;j>0;j--){
if(array[j] < array[j-1]){
temp = array[j-1];
array[j-1] = array[j];
array[j] = temp;
}else{ //不需要交换
break;
}
}
}
}
快速排序
public static void quickSort(int a[],int l,int r){
if(l>=r)
return;
int i = l; int j = r; int key = a[l];//选择第一个数为key
while(i<j){
while(i<j && a[j]>=key)//从右向左找第一个小于key的值
j--;
if(i<j){
a[i] = a[j];
i++;
}
while(i<j && a[i]<key)//从左向右找第一个大于key的值
i++;
if(i<j){
a[j] = a[i];
j--;
}
}
//i == j
a[i] = key;
quickSort(a, l, i-1);//递归调用
quickSort(a, i+1, r);//递归调用
}
递归排序
Heap Sort
heap: orderd binary tree
max heap: parent > child
public class HeapSort
{
public void sort(int arr[])
{
int n = arr.length;
// Build heap (rearrange array)
for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
// One by one extract an element from heap
for (int i=n-1; i>=0; i--)
{
// Move current root to end
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// call max heapify on the reduced heap
heapify(arr, i, 0);
}
}
// To heapify a subtree rooted with node i which is
// an index in arr[]. n is size of heap
void heapify(int arr[], int n, int i)
{
int largest = i; // Initialize largest as root
int l = 2*i + 1; // left = 2*i + 1
int r = 2*i + 2; // right = 2*i + 2
// If left child is larger than root
if (l < n && arr[l] > arr[largest])
largest = l;
// If right child is larger than largest so far
if (r < n && arr[r] > arr[largest])
largest = r;
// If largest is not root
if (largest != i)
{
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
// Recursively heapify the affected sub-tree
heapify(arr, n, largest);
}
}
/* A utility function to print array of size n */
static void printArray(int arr[])
{
int n = arr.length;
for (int i=0; i<n; ++i)
System.out.print(arr[i]+" ");
System.out.println();
}
// Driver program
public static void main(String args[])
{
int arr[] = {12, 11, 13, 5, 6, 7};
int n = arr.length;
HeapSort ob = new HeapSort();
ob.sort(arr);
System.out.println("Sorted array is");
printArray(arr);
}
}
BFS
private static void bfs(HashMap<Character, LinkedList<Character>> graph, HashMap<Character, Integer> dist,
char start) {
Queue<Character> q = new LinkedList<>();
q.add(start);// 将s作为起始顶点加入队列
dist.put(start, 0);
int i = 0;
while (!q.isEmpty()) {
char top = q.poll();// 取出队首元素
i++;
System.out.println("The " + i + "th element:" + top + " Distance from s is:" + dist.get(top));
int d = dist.get(top) + 1;// 得出其周边还未被访问的节点的距离
for (Character c : graph.get(top)) {
if (!dist.containsKey(c))// 如果dist中还没有该元素说明还没有被访问
{
dist.put(c, d);
q.add(c);
}
}
}
}
DFS
private static void dfs(HashMap<Character, LinkedList<Character>> graph, HashMap<Character, Boolean> visited) {
visit(graph, visited, 'u');// 为了和图中的顺序一样,我认为控制了DFS先访问u节点
visit(graph, visited, 'w');
}
//通过一个全局变量count记录了进入每个节点和离开每个节点的时间
static int count;
private static void visit(HashMap<Character, LinkedList<Character>> graph, HashMap<Character, Boolean> visited,
char start) {
if (!visited.containsKey(start)) {
count++;
System.out.println("The time into element " + start + ":" + count);// 记录进入该节点的时间
visited.put(start, true);
for (char c : graph.get(start)) {
if (!visited.containsKey(c)) {
visit(graph, visited, c);// 递归访问其邻近节点
}
}
count++;
System.out.println("The time out element " + start + ":" + count);// 记录离开该节点的时间
}
}
系统设计
CAP
Consistency(一致性): 数据一致更新,所有数据变动都是同步的
Availability(可用性): 好的响应性能
Partition tolerance(分区耐受性): 可靠性
上面的解释可能显得太过抽象,举例来说在高可用的网站架构中,对于数据基础提出了以下的要求:
分区耐受性
保证数据可持久存储,在各种情况下都不会出现数据丢失的问题。为了实现数据的持久性,不但需要在写入的时候保证数据能够持久存储,还需要能够将数据备份一个或多个副本,存放在不同的物理设备上,防止某个存储设备发生故障时,数据不会丢失。
数据一致性
在数据有多份副本的情况下,如果网络、服务器、软件出现了故障,会导致部分副本写入失败。这就造成了多个副本之间的数据不一致,数据内容冲突。
数据可用性
多个副本分别存储于不同的物理设备的情况下,如果某个设备损坏,就需要从另一个数据存储设备上访问数据。如果这个过程不能很快完成,或者在完成的过程中需要停止终端用户访问数据,那么在切换存储设备的这段时间内,数据就是不可访问的。
CAP原理认为,一个提供数据服务的存储系统无法同时完美的满足一致性(Consistency)、数据可用性(Availability)、分区耐受性(Partition Tolerance)这三个条件。
数据一致性:
数据强一致
各个副本中的数据总是强一致的。这种设计正确性很高,但是会在一定程度上损耗性能。
数据用户一致
应用访问数据时通过一定的纠错和校验机制,把多个数据可能不一致的副本的数据综合计算返回一个一致且确定的数据给用户。大型互联网架构一般采用这种设计,性能较好,并且数据不会出现错误。
数据最终一致
物理存储的数据不一致,用户访问得到的数据也可能不一致,但经过一段时间的自我修正(通常很短时间),数据会达到最终一致。该设计性能最高,但可能有数据错误。