数据结构与算法

大O符号:Big O notation,是由德国数论学家保罗·巴赫曼在其1892年的著作《解析数论》首先引入的
指数函数:幂 = 2N
对数函数:指数 = log2N,log10N简写为lgN,logeN简写为lnN
对数的底:logaN中,增长率主要取决于N,因此O(logaN)简写为O(logN)
平均时间复杂度:常数 c、对数 logN、对数平方log2N、线性N、NlogN、平方N2、指数 2N

链表 List
ArrayList:数组实现,查找和更新 O(c),添加和删除 O(N)
LinkedList:双链表实现,查找和更新 O(N),添加和删除 O(c)

栈 Stack
可以把双链表当做栈来操作,即后进先出

队列 Queue
可以把双链表当做队列来操作,即先进先出

树的遍历
先序遍历:先处理本节点,再处理子节点
中序遍历:先处理左子树,在处理本节点,最后处理由子树
后序遍历:先处理子节点,再处理本节点

二叉树
每个枝节点最多有两个子节点,二叉树的平均高度为O(√N),N为节点数

二叉查找树
每个枝节点,它的左子节点小于它,它的右子节点大于它
二叉查找树的节点的平均高度为O(logN),即查找、新增的平均时间为O(logN),最坏O(n)
二叉查找树节点删除:删除A节点的左子节点B,则查找B节点的右子树的最小节点C,来替换B。如果C不是叶节点,则递归删除。
懒惰删除:不实际删除节点,而是标记为假删除

AVL树(取名于两个发明者名字的首字母)
带有平衡条件的二叉查找树,保证每个节点的左子树和右子树的高度最多差1
实现:A有左子节点B,没有右子节点,要给B插入左子节点C,会导致A失衡。让B代替A的位置,A作为B的右子节点。最终为B有左子节点C,右子节点A

B树(Balanced Tree of Order M,多叉平衡搜索树)
1、叶节点是个数组,数据项只保存在叶节点的数组中
2、枝节点保存两组数据,形如[100,200] [子节点1,子节点2,子节点3]
3、子节点1的数据范围在(,100],子节点2的数据范围在[100,200],子节点3的数据范围在[200,)

红黑树
带有颜色标记的二叉查找树,get\add\remove的最坏时间复杂度O(logN)
1、每个节点要么是红色,要么是黑色。根节点是黑色
2、红色节点不能连续
3、任一节点,向下到树末梢的任何路径,都含有相同个数的黑色节点
与二叉查找树对比:任一子树的最长路径不大于最短路径的两倍,保证了查找速度
与AVL树对比:降低了平衡条件,任何不平衡都会在三次旋转之内解决,使得插入和删除的性能更高

散列

散列三要素
散列函数:输入key,输出一个散列值,例如取余函数
散列表:存放数据的空间
散列冲突解决方案:解决多条数据的散列值相同的问题

分离链接法(拉链法):把散列值相同的数据放到链表中
线性探测法:一条数据在散列表上的位置已经被占据,则把他放到下一个存储单元
双散列:一条数据在散列表上的位置已经被占据,则把它放到2倍散列值的位置上

JAVA里的散列实现
数组作为散列表,数组的一项作为桶,拉链法解决散列冲突。hashCode()相同的放入一个桶,hashCode()相同并equals()的,当做重复元素。数组有一个初始长度,数据量到达阈值时,扩容resize到原来的两倍,并重新散列rehash。除非发生扩容,否则操作时间复杂度O(1),比红黑树快。
以HashMap为例,构造时可以传入两个参数initialCapacity和loadFactor,即初始容量和扩容阈值,默认为16和0.75。最佳初始容量为 预期数据量 / loadFactor + 1。

无重复集合 Set
TreeSet:红黑树实现,有序,元素需要实现Comparable接口的compareTo方法,不能放入null
HashSet:散列实现,可以放入一个null
LinkedHashSet:散列表存放数据,链表记录插入顺序,顺序与TreeSet不同

映射 Map
TreeMap:红黑树实现
HashMap:散列实现,key、value都可以为null。
Hashtable:散列实现,key、value都不可以为null。每个操作方法都声明了Synchronize,线程安全,同时性能也稍低。
ConcurrentHashMap:散列实现,线程安全,迭代时只锁死部分元素,比Hashtable性能稍高

优先队列 priority queue
优先队列通常用二叉堆来实现,因此二叉堆可以指代优先队列,简称堆

二叉堆 binary heap
完全二叉树:除了最后两层,所有节点都有两个子节点,且最底层节点从左到右紧密排列。可以用树实现,可一样用数组实现。
二叉堆:一个完全二叉树,它的每棵子树的根节点都是这个子树的最小元素
PriorityQueue:数组式二叉堆实现

排序(以下排序说的都是升序)

双循环法

冒泡排序 平均O(n2),最坏O(n2)

// 任一时刻,数组的后部分已排好序
for (var i = 0; i < len; i++) {
 for (var j = 0; j < len - i - 1; j++) {   // 遍历前部分
  if (arr[j] > arr[j+1]) {                   // 对比相邻元素,让大元素往后冒泡,加入到后部分
   var temp = arr[j+1];  
   arr[j+1] = arr[j];
   arr[j] = temp;
  }
 }
}

选择排序 平均O(n2),最坏O(n2)

var len = arr.length;
// 任一时刻,数组的前部分已排好序
for (var i = 0; i < len - 1; i++) {
 var minIndex = i;
 for (var j = i + 1; j < len; j++) {  // 遍历后部分,找到最小值
  if (arr[j] < arr[minIndex]) {
    minIndex = j; 
  }
 }
 var temp = arr[i];                   // 把找到的最小值加到前部分
 arr[i] = arr[minIndex];
 arr[minIndex] = temp;
}

插入排序 平均O(n2),最坏O(n2)

// 任一时刻,数组的前部分已排好序
for (var i = 1; i < array.length; i++) {
 var cur = array[i];
 for(var j=i-1; array[ j ] > cur; j-- ) {    // 从后往前遍历前部分,将当前元素插入到前部分
   array[ j + 1] = array[ j ];            // 比当前元素大的都往后移动一格
 }
 array[ j + 1] = cur;      // 将当前元素插入
}
分组法

希尔排序 平均O(nlogn),最坏O(nlog2n)

var len = arr.length, gap = 1;
while(gap < len / 5) {  // 动态定义初始间隔
  gap =gap*5+1;    // 此计算为实践经验,gap和length的关系为 [1,1-5] [6, 6-30] [31, 31-155]
}
while (gap > 0) {
   // 位置相差gap的元素分为一组,对每组进行插入排序
 for (var i = gap; i < len; i++) {  // i 每加1,就进入到了另外一组,所以对每组的排序 是 穿插进行的
  var temp = arr[i];
  for (var j = i-gap; j >= 0 && arr[ j ] > temp; j-=gap) {
   arr[ j+gap ] = arr[ j ];
  }
  arr[ j+gap ] = temp;
 }
    gap = Math.floor(gap / 5);  // gap最后一次是 1,就是一个插入排序,但此时元素移动可以大大减少
}

归并排序 平均O(nlog n),最坏O(nlog n)

function mergeSort(arr) {
  var len = arr.length;
  if(len < 2) {      // 拆分到只有一个元素时,就当做是排好序的数组
    return arr;
  }
  var middle = Math.floor(len / 2),   // 把数组拆分成两半
  left = mergeSort(arr.slice(0, middle)), 
  right = mergeSort(arr.slice(middle));  // 两半分别排好序
  return merge(left, right);   // 将排好序的两半进行归并
}
function merge(left, right){
  var result = [];
  while (left.length && right.length) {
    if (left[0] <= right[0]) {
      result.push(left.shift());
    } else {
      result.push(right.shift());
    }
  }
  while (left.length){
    result.push(left.shift());
  }
  while (right.length){
    result.push(right.shift());
  }
  return result;
}

快速排序 平均O(n*log n),最坏O(n2)

function quickSort(array, start, end) {
  if (start < end) {       // 当 start 等于end 时,就是拆分到了一个元素
    var last = array[end],          // 用最后一个元素 跟 其他元素比较
                    i = start - 1;                   // 标记比last小的元素存放的位置,目前还没有,所以是 -1
    for (var j = start; j <= end; j++) {   // 遍历整个数组
      if (array[ j ] <= last ) {   // 当前元素小于等于last,则交换到位置 i
        i++;
        var temp = array[ i ];
        array[ i ] = array[ j ];
        array[ j ] = temp;
      }
    }
              // 将数组拆分成两个数组,分别排序,其中一个数组都小于等于last,另一个都大于last 
    quickSort(array, start, i - 1);  // start 到 i - 1都小于等于last
              // 中间还有个array[ i ]就是 last
    quickSort(array, i + 1, end);  // i + 1到end都大于last
  }
  return array;
}
quickSort(arr,0,arr.length-1);
分桶法

以下k表示桶数

计数排序 平均O(n+k),最坏O(n+k)
把数据的值作为下标,放到一个新数组中,最后只要顺次读取新数组
限制:桶的数量(新数组的长度)不小于数据的取值范围

桶排序 平均O(n+k),最坏O(n2)
1、遍历数组,将相同级别的数据放到一个桶中
2、分别排序每一个桶
3、读出每一个桶的数据
例如:100以内数字的排序,根据数字的十位数,分别放到编号0-9的数组中,分别对每个数组排序,依次读出每个桶里的数字

基数排序 平均O(nk),最坏O(nk)
1、遍历数组,将相同尾数的数据放到一个桶中
2、按顺序读出所有数据,再根据次尾数分别放到桶中,以此类推
3、按顺序读出所有数据
例如:对[12,11,2,1]排序,放入两个桶[11,1][12,2],读出[11,1,12,2],再次入桶[1,2][11,12],再次读出[1,2,11,12]

二叉树法

堆排序 平均O(nlog n),最坏O(nlog n)
即每次从二叉堆中取出堆顶元素

常见问题与方案

topK

问题:从N个数中,找到最大的K个数
方案:读取前K个数,创建一个堆顶为最小数的堆;读取后N-K个数,比堆顶大则删除堆顶,并插入堆;时间复杂度为O(N*logK)

寻找中位数

等同于找出 top N/2

一个整数的二进制表示中1的个数

while(n>0){
if((n&1)==1){result++}
n = n>>1 // 每次消除一位
}

while(n>0){
result ++;
n = n&(n-1) // 每次消除一个1
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容

  • 数据结构学科的定义:主要是为研究和解决如何使用计算机处理非数值问题而产生的理论、技术和方法。数据结构:是相互之间存...
    冷了年度阅读 1,591评论 0 0
  • 前言 上一篇《数据结构和算法》中我介绍了数据结构的基本概念,也介绍了数据结构一般可以分为逻辑结构和物理结构。逻辑结...
    VV木公子阅读 4,662评论 4 41
  • 算法引入:如果a+b+c=1000,且a2+b2=c^2(a,b,c为自然数),如何求出所有a,b,c可能的组合?...
    Bling_ll阅读 510评论 0 3
  • 印度有四句极具灵性的话: 无论你遇见谁,他都是对的人; 无论发生什么事,那都是唯一会发生的事; 不管事情开始于哪个...
    李思睿vicky阅读 1,187评论 0 1
  • 上次分手改了名字。两年心里很执拗。现在放弃了。由于今天早上公交司机的话。还有昨天。所以。不好意思对不起。 工作的事...
    0401阅读 418评论 0 0