本文会了解到:
- 什么是数据结构
- 什么是算法
- 数据结构和算法之间什么关系
- 如何衡量算法的优劣
- 基础数据结构有哪些
- 如何实现?
本文的思维导图:
一句话说明什么是数据结构: they are different methods of storing and organizing data that serve a number of different needs.
-
- 对数据结构常用的操作有 : 增删改查
-
- It is important to note that data structures may be good at one action but bad at another
一句话说明什么是算法: is a fancy name for step-by-step sets of opeartions to be performed
-
- 算法就是指令合集,做一件事情有很多解决办法
那如何衡量算法的好坏? Big O Notation 从两个方面判断
-
Time complexity refers to the total count of operations an algorithm will perform given a set of items.
-
Space complexity refers to the total memory an algorithm will take up while running given a set of items.
-
We measure these independently from one another because while an algorithm may perform fewer operations than another, it may also take up way more memory. Depending on what your requirements are, one may be a better choice than the other.
所以数据结构和算法是你中有我我中有你的关系。
- 解决一个问题的基本步骤:
-
- 明确问题
-
- 确定适合的数据结构
-
- 选择合适的算法
-
- 过一遍思路
-
- 代码实现
-
- 测试和优化
那为什么要有怎么多数据结构? 因为计算机只认识 0 1 ,数据结构在逻辑层面做了一层抽象,方便开发者使用,而不用关系细节, 数据结构按照逻辑分可以分为线性表和非线性表,按存储方式分可以分为连续和非连续。
常用的基础的数据结构有:
如何实现?
这里使用js实现,其他语言类似
1 js实现list
- list的属性和方法:
class List {
constructor() {
this.memory = [];
this.length = 0;
}
get(address) {
return this.memory[address];
}
//"push" and "pop" both operate on the end of a list, and overall are pretty
// simple operations because they don't need to be concerned with the rest of
//the list.
push(value) {
this.memory.push(value);
}
isEmpty() {
return this.length == 0;
}
pop() {
if (this.isEmpty()) return;
let lastAddress = this.length - 1;
let value = this.memory[lastAddress];
delete this.memory[lastAddress];
this.length--;
return value;
}
//In order to slide all of the items over we need to iterate over each one
//moving the prev value over.
//Because we have to iterate over every single item in the list:
// Unshifting an item to the start of a list is linear O(N) - "OKAY."
unshift(value) {
let previous = value;
//iterate through each item...
for (let address = 0; address < this.length; address++) {
//replacing the "current" value with the "previous" value and stroing the "current"
//value for the next iteration
let current = this.memory[address];
this.memory[address] = previous;
previous = current;
}
//add the last item in a new posotion at the end of the list .
this.memory[this.length] = previous;
this.length++;
}
shift() {
if (this.isEmpty()) return; //do nothing
let value = this.memory[0];
for (let address = 0; address < this.length - 1; address++) {
//repalce them with the next item in the list
this.memory[address] = this.memory[address + 1];
}
delete this.memory[this.length - 1];
this.length--;
}
}
// Lists are great for fast access and dealing with items at the end. However,
// as we've seen it isn't great at dealing with items not at the end of the
// list and we have to manually hold onto memory addresses.
module.exports = {
List
};
list 访问的时间复杂度是O(1) 其他增 删 改的平均时间复杂度是O(n)
2 js实现 stack 和 queue.
stack 遵循的是LIFO (last-in, first-out) stack wiki
queue遵循的是 first-in-first-out (FIFO) queue wiki 熟悉这些特点对后续的使用很有必要
class Stack {
constructor() {
this.list = [];
this.length = 0;
}
isEmpty() {
return this.length == 0;
}
peek() {
if (this.isEmpty()) return;
return this.list[this.length - 1];
}
push(value) {
this.length++;
this.list.push(value);
}
pop() {
if (this.isEmpty()) return;
this.length--;
this.list.pop();
}
}
module.exports = {
Stack
};
class Queue {
constructor() {
this.list = [];
this.length = 0;
}
enqueue(value) {
this.length++;
this.list.push(value);
}
dequeue() {
if (this.length === 0) return;
this.length--;
//shift is O(N) later will use a linked list to replace array
return this.list.shift();
}
peek() {
return this.list[0];
}
}
module.exports = {
Queue
};
3 js实现hastTable
- HastTable的属性和方法:
- 哈希冲突问题, 这里不展开
- Option 1: By having each bucket contain a linked list of elements that are hashed to that bucket. This is why a bad hash function can make lookups in hash tables very slow.
- Option 2: If the hash table entries are all full then the hash table can increase the number of buckets that it has and then redistribute all the elements in the table. The hash function returns an integer and the hash table has to take the result of the hash function and mod it against the size of the table that way it can be sure it will get to bucket. So by increasing the size, it will rehash and run the modulo calculations which if you are lucky might send the objects to different buckets.
Java uses both option 1 and 2 in its hash table implementations.
class HashTable {
constructor() {
this.memory = [];
}
//hashing
/**
* fing key-value pair
*
* if key too big we don;t want to store it so we need to
* limit the size
*
* sometimes two keys get the same address
* we need to solve the collisions
*/
hashKey(key) {
let hast = 0;
for (let index = 0; index < key.length; index++) {
let code = key.chatCodeAt(index);
hast = ((hash << 5) - hash + code) | 0;
}
return hash;
}
//access T O(1)
get(key) {
let address = this.hashKey(key);
//we simply return whatever is at that address
return this.memory[address];
}
//set T O(1)
set(key, value) {
let address = this.hashKey(key);
this.memory[address] = value;
}
// deletion is constant O(1)
remove(key) {
let address = this.hashKey(key);
if (this.memory[address]) {
delete this.memory[address];
}
}
}
module.exports = {
HashTable
};
4 js实现linkedList
- linkedList的属性和方法:
/**
*
* 1-> 2 -> 3 -> 4 -> 5
*
* visualizing them as a JSON-like structure looks like this
*
* {
* value : 1,
* next:{
* value : 2,
* next :{
* value : 3,
* next :{ ...}
* }
*
* }
* }
*/
class LinkedList {
constructor() {
this.head = null;
this.length = 0;
}
get(position) {
if (position > this.length) {
throw new Error("Position outside of list range ");
}
//start with the head of the list
let current = this.head;
for (let index = 0; index < position; index++) {
current = current.next;
}
// index = position
// rurturn the node we found
return current;
}
add(value, position) {
// create a node to hold our value
let node = {
value,
next: null
};
//special case for being insterted at the head
//we'll set the next field to the current head
//and then replace it with our new node
if (position == 0) {
node.next = this.head;
this.head = node;
//if we are adding a node in any other position we need to splice it
//in between the current node and the previous node
} else {
//get prev node
let prev = this.get(position - 1);
let current = prev.next;
//先处理下一个
node.next = current;
//再处理它的上一个
prev.next = node;
}
this.length++;
}
remove(position) {
//we should noe be able to remove from an empty list
if (!this.head) {
throw new Error("Removing from empty list");
}
if (position === 0) {
this.head = this.head.next;
} else {
let prev = this.get(position - 1);
// prev.next is current
prev.next = prev.next.next;
}
this.length--;
}
}
module.exports = {
LinkedList
};
总结
- 本文介绍了什么是数据结构和算法
- 以及用js实现了几个基本的数据结构, 希望有帮助
- 后续需要自己更多的熟悉以及实现
- 熟悉这些基础是后面实现各种算法的基础。
- 我是前端关宇,感谢阅读!