数据结构学习笔记(JavaScript 描述)

in #cn7 years ago

常用的数据结构包括数组、列表、栈、队列、链表、字典、散列、集合等。每种数据结构都有其自己的特点。很多时候,选择一个恰当的数据结构,可以很大程度简化棘手的问题。

  1. 数组
    1. 常用方法
    2. 二维数组
  2. 列表
  3. 队列
  4. 链表
  5. 字典
  6. 散列
  7. 集合

数组

数组的定义是:一个存储元素的线性集合,元素可以通过索引任意存取。索引通常是数字。然而在 JavaScript 中,数组是一种特殊的对象,索引为该对象的属性,而 JavaScript 中对象的属性名必须是字符串类型。看上去很奇怪,不过也正因此,数组可以作为其他数据结构的基础。

常用方法

创建数组的方法有很多,不过一般推荐使用 [] 操作符。

var nums = []

nums.unshift(num)   //在开头添加元素
nums.shift(num)     //删除开头元素
nums.pop()          //删除末尾元素
nums.push(num)      //在末尾添加元素
nums.splice(start, n, elements)     //在中间 `start` 位置删除 n 个元素,并添加若干个 elements。
nums.reverse()      //反序
nums.sort()         //将数字从小到大排序

二维数组

按照《JavaScript 精髓》一书中的做法,通过扩展 JavaScript 数组对象实现。

Array.matrix = function(numrows, numcols, initial) {
    var arr = []
    for (var i = 0; i < numrows; ++i) {
        var columns = []
        for (var j = 0; j < numcols; ++j) {
            columns[j] = initial
        }
        arr[i] = columns
    }
    return arr
}

列表

列表是一组有序的数据,适合用于保存的数据不太多时。

列表的抽象数据类型定义:

listSize pos length 
insert() append() remove() clear()
front() end() currPos() moveTo()
prev() next() hasPrev() hasNext() 
toString() getElement() contains()

栈是一种特殊的列表,栈内的元素只能通过列表的一端访问。遵循后入先出的数据结构。因此,只能访问栈顶的元素,即最后加入的元素。

push()  //向栈内压入元素
pop()   //删除栈顶元素
peek()  //返回栈顶元素而不删除它

队列

队列也是一种列表,不同的是队列遵循先进先出的数据结构,即只能在队尾插入元素,在队首删除元素。

enqueue()   //向队尾插入元素,即 push()
dequeue()   //删除队首元素,即 shift()
front()     //返回队首元素
back()      //返回队尾元素

链表

与列表的是,链表的底层数据结构不是数组,而是节点。与数组相比,链表的性能更加优越。缺陷是不方便进行随机访问。数组元素通过位置来进行引用,而链表元素则通过相互关系来引用。每一个节点都包含元素数据和指向下个节点的链接。

// Node 类
function Node() {
    this.element = element
    this.next = null
}
// 链表类
function LinkedList() {
    this.head = new Node("head")    //初始化头节点
    this.find = find        //查找
    this.insert = insert    //插入节点
    this.remove = remove    //移除节点
    this.display = display  //显示
}


双向链表

为了能够方便的从后往前遍历,为每个节点添加指向前驱的链接。

// Node 类
function Node() {
    this.element = element
    this.next = null
    this.previous = null
}

循环链表

与单向列表相似,唯一的区别在于初始化链表时,头节点的 next 属性指向它本身。这种行为会传导下去,这样,链表的尾节点就会指向头节点。

function LinkedList() {
    this.head = new Node("head")    //初始化头节点
    head.next = head    // 头节点 next 属性指向自身
    //...
}

字典

字典通过键-值对来存储数据。可以利用 JavaScript Object 类本身的特性来实现 Dictionary 类。使用 () 操作符来引用,而不是 []。

function Dictionary() {
    this.datastore = []
    this.add = add  // add(key, value),即 this.datastore[key] = value
    this.find = find
    this.remove = remove
    this.showAll = showAll
    this.count = count
    this.clear = clear
}

散列

散列是一种可以快速插入或取用的数据存储技术。散列使用的数据结构称为散列表。使用散列表存储时,通过有个散列函数讲键映射为一个数字,数字范围是 0 到散列表的长度。散列表的长度是预先定义的,一般取质数。当两个值映射到同一个值时则会发生碰撞

散列算法

除留余数法:最简单的方法。以数组的长度对键取余(当键值为字符串时取其 ASCII 码之和),为了尽量避免结果相等发生碰撞,所以数组长度一般取质数。

霍纳算法:除留余数法很容易会发生碰撞。而霍纳算法则可以更好地避免这个问题。霍纳算法仍然计算字符串中各个字符的 ASCII 码值,不过在求和时每次都要乘一个质数,一般建议使用一个较小的质数。

function HornerHash(string, arr) {
    const H = 31
    var total = 0
    for (var i=0; i<string.length; ++i) {
        total += H*total + string.charCodeAt(i)
    }
    total = total % arr.length
    return parseInt(total)
}

碰撞处理

开链法:在实现散列表的底层数组时,每个数组元素又是一个新的数据结构,这样就可以存储多个键了,当两个键散列后的数值相同时,依然可以保存在相同的位置。
开链法

线性探测法:线性探测法属于一种更一般的散列技术:开放寻址散列。当发生碰撞时,线性探测法会检查散列表中下一个位置是否为空,如果为空,则填入数据,否则,继续检查下一个位置,知道找到空位并填入。

一般来说,如果数组的大小是到存储数据个数的1.5倍,则使用开链法。如果数组大小为待存储数据的两倍及以上,则使用线性探测法。

集合

集合是一种包含不同元素的数据结构。集合具有两个特性。首先,集合元素是无序的,其次,集合中不允许相同成员存在。

集合的几个基本操作:并集、交集、补集、子集。


Originally posted on https://steemit.com . Thanks for reading my post, please feel free to follow, upvote, reply, resteem @hopsken , which will motivate me to create more quality posts. https://steemit.com 首发。感谢阅读,欢迎follow, upvote, reply, resteem @hopsken 鼓励我创作更多更好的内容。
Sort:  

还是java好啊 ,c中除了数组,其余的都得自己去实现.

emm各有各的优点嘛😂