一、数据结构的存储方式
1. 数据结构的基本存储方式 :【数组】(顺序存储)和【链表】(链式存储)
但实际上朗朗上口的数据结构 还有很多 【栈】 【队列】 【散列表】但其实实际上都是基于 【数组/链表】实现的。
下面举两个例子
1.散列表:
通过散列函数把键映射到一个大数组里。而且对于解决散列冲突的方法,拉链法需要链表特性,操作简单,但需要额外的空间存储指针;线性探查法就需要数组特性,以便连续寻址,不需要指针的存储空间,但操作稍微复杂些。
2.栈/队列:
这两种数据结构既可以使用链表也可以使用数组实现。用数组实现,就要处理扩容缩容的问题;用链表实现,没有这个问题,但需要更多的内存空间存储节点指针。
3.堆/树:
堆:可以被看做一棵完全二叉树的数组对象。
树:用链表实现【衍生出各种巧妙的设计,比如二叉搜索树、AVL 树、红黑树、区间树、B 树等等,以应对不同的问题。】
4.图:
邻接表:链表实现。 邻接表比较节省空间,但是很多操作的效率上肯定比不过邻接矩阵。
邻接矩阵:二维数组实现。 邻接矩阵判断连通性迅速,并可以进行矩阵运算解决一些问题,但是如果图比较稀疏的话很耗费空间。
二、数据结构的基本操作
1.对于任何数据结构,其基本操作无非遍历 + 访问,再具体一点就是:增删查改。
2.各种数据结构的遍历 + 访问无非两种形式:线性的【迭代】和非线性的【递归】。
三、数据结构操作的基本框架
以下给出几种常见的遍历操作方式
1. 数组遍历框架,典型的线性迭代结构:
/** * @param {number[]} arr */ function go(arr) { for (var i = 0; i < arr.length; i++) { // 迭代访问 arr[i] } }
2. 链表遍历框架,兼具迭代和递归结构:
/** * Definition for ListNode. * function ListNode (val) { * this.val = val; * this.next= null; * } */ /** * @param {ListNode } head */ //go1迭代 function go1(head) { //方式一 for循环 for (let p = head; p != null; p = p.next) { // 迭代访问 p.val 【迭代操作】 } //方式二 while循环 while(head){ // 迭代访问 head.val 【迭代操作】 head=head.next } } //go2递归 function go2(head) { // 递归访问 head.val 【迭代操作】 go2(head.next) }
3. 二叉树遍历框架,典型的非线性递归遍历结构:【扩展 树的遍历】
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ function go(root) { //递归时 操作可在任意位置放置 //例如【前、中、后序 遍历】 只需分别将root.val 放于递归操作 前 中 后即可 //root.val 【前序遍历】 go(root.left) //root.val 【中序遍历】 go(root.right) //root.val 【后序遍历】 }
3.1 由上面看出N叉树遍历 只需按照子节点顺序遍历即可
/** * Definition for a tree node. * function Node(val,children) { * this.val = val; * this.children = children; * }; */ /** * @param {Node} root */ function go(root) { //递归时 操作可在任意位置放置 for(let i of root.children) go(i) }