0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

为什么要使用数据结构和算法

算法与数据结构 来源:腾讯AlloyTeam Blog byTAT. 作者:h1654155167.6972 2022-11-14 11:24 次阅读

数据结构是对在计算机内存中(有时在磁盘中)的数据的一种安排。包括数组、链表、栈、二叉树、哈希表等。

算法是对这些结构中的数据进行各种处理。比如,查找一条特殊的数据项或对数据进行排序。

举一个简单的索引卡的存储问题,每张卡片上写有某人的姓名、电话、住址等信息,可以想象成一本地址薄,那么当我们想要用计算机来处理的时候,问题来了:

●如何在计算机内存中安放数据?

●所用算法适用于100张卡片,很好,那1000000张呢?

●所用算法能够快速插入和删除新的卡片吗?

●能够快速查找某一张卡片吗?

●如何将卡片按照字母进行排序呢?

事实上,大多数程序比地址簿要复杂得多,想象一下航班预订系统的数据库,存储了旅客和航班的各种信息,需要许多数据结构组成。如果您很清楚这些问题,那么请您对我的博文给出宝贵意见,如果不清楚,那么在我的博文中可以给您一些适当的指引。

随着NodeJs技术的发展,可以在服务器端使用javascript,控制MongoDB进行持久化数据存储。这就需要一些复杂的数据结构和算法来提高程序的性能,仅仅使用数组和for循环来处理数据是远远不够的。

数据结构概述


数据结构 优点 缺点
数组 插入快,如果知道下标,可以非常快的存取 查找慢,删除慢,大小固定
有序数组 比无序数组查找快 删除和插入慢,大小固定
提供“后进先出”的存取方式 存取其他项很慢
队列 提供“先进先出”的存取方式 存取其他项很慢
链表 插入快,删除快 查找慢
二叉树 如果树保持平衡,查找、插入、删除都很快 删除算法比较复杂
红-黑树 树总是平衡的,查找、插入、删除都很快 算法比较复杂
2-3-4树 对磁盘存储有用,树总是平衡的,查找、插入、删除都很快 算法比较复杂
哈希表 插入快,如果关键字已知则存取极快 删除慢,如果不知道关键字则存取很慢,对存储空间使用不充分
插入、删除快,对最大数据项的存取很快 对其他数据项存取慢
对现实世界建模 有些算法慢且复杂

算法概述

对大多数数据结构来说,都需要知道如何:

●插入一条新的数据项

●查找某一个特定的数据项

●删除某一个特定的数据项

●遍历某一数据结构中的各数据项

●排序

另外,递归的概念在设计有些算法时,也是十分重要的。

javascript面向对象编程

博文中的数据结构均被实现为对象,本节是为了给那些还没有接触过面向对象编程的读者准备的,但是,短短的一节并不能涵盖所有面向对象编程的思想,仅仅是让读者能够明白博文中的代码示例。

Javascript是一种基于对象的语言,但是,又不是一种真正意义上的面向对象的语言,因为没有class(类)的语法。

一、创建对象

创建对象就是把属性(property)和方法(method)封装成一个对象,或者从原型对象中实例化一个对象。

下面以实例化小狗对象为例,小狗具有名字和品种两个属性。

1、原始模式

var dog1 = {};

dog1.name = '二牛';

dog1.variety = '牛头梗';

var dog2 = {};

dog2.name = '二狗';

dog2.variety = '哈士奇';

这样封装对象虽然简单,但是有两个缺点,一是如果要创建多个实例的话,写起来会比较麻烦,二是这种写法并不能看出实例和原型之间有什么关系。

对原始模式进行改进,

function Dog(name, variety) {

return {

name: name,

variety: variety

};

}

var dog1 = Dog('二牛', '牛头梗');

var dog2 = Dog('二狗', '哈士奇');

改进后解决了代码重复的问题,但是dog1和dog2之间并没有内在联系,不是来自于同一个原型对象。

2、构造函数模式

构造函数,是内部使用了this的函数。通过new构造函数就能生成对象实例,并且this变量会绑定在实例对象上。使用构造函数可以解决从原型对象构建实例的问题。

function Dog(name, variety) {

this.name = name;

this.variety = variety;

}

var dog1 = new Dog('二牛', '牛头梗');

var dog2 = new Dog('二狗', '哈士奇');

print(dog1.name); // 二牛

print(dog2.name); // 二狗

验证实例对象与原型对象之间的关系:

print(dog1.cunstructor === Dog); // true

print(dog2.cunstructor === Dog); // true

print(dog1 instanceof Dog); // true

print(dog2 instanceof Dog); // true

这样看来构造函数模式解决了原始模式的缺点,但是它自己又引入了新的缺点,就是有些时候存在浪费内存的问题。比如说,我们现在要给小狗这个对象添加一个公共的属性“type”(种类)和一个公共方法“bark”(吠):

function Dog(name, variety) {

this.name = name;

this.variety = variety;

this.type = '犬科';

this.bark = function() {

print('汪汪汪');

}

}

再去实例化对象,

var dog1 = new Dog('二牛', '牛头梗');

var dog2 = new Dog('二狗', '哈士奇');

print(dog1.type); // 犬科

dog1.bark(); // 汪汪汪

print(dog2.type); // 犬科

dog2.bark(); // 汪汪汪

这样看似没有问题,那么我写另一段代码来看一下问题所在,

print(dog1.bark() === dog2.bark()); // false

从中我们可以看出问题,那就是对于每个实例对象而言,type属性和bark方法都是一样的,但是每次创建新的实例,都要为其分配新的内存空间,这样做就会降低性能,浪费空间,缺乏效率。

接下来我们就要思考怎样让这些所有实例对象都相同的内容在内存中只生成一次,并且让所有实例的这些相同内容都指向那个内存地址?

3、Prototype模式

每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。可以利用这一点,把那些不变的属性和方法,定义在prototype对象上。

function Dog(name, variety) {

this.name = name;

this.variety = variety;

}

Dog.prototype.type = '犬科';

Dog.prototype.bark = function() {

print('汪汪汪');

}

var dog1 = new Dog('二牛', '牛头梗');

var dog2 = new Dog('二狗', '哈士奇');

print(dog1.type); // 犬科

dog1.bark(); // 汪汪汪

print(dog2.type); // 犬科

dog2.bark(); // 汪汪汪

print(dog1.bark() === dog2.bark()); // true

这里所有实例对象的type属性和bark方法,都指向prototype对象,都是同一个内存地址。

二、继承

现在有一个动物的构造函数:

function Animal() {

this.feeling = 'happy';

}

有一个小狗的构造函数:

function Dog(name, variety) {

this.name = name;

this.variety = variety;

}

以下如不对Animal和Dog对象进行重写,则使用该代码进行代入,示例代码中不再重复。

1、原型链继承

Dog.prototype = new Animal();

Dog.prototype.constructor = Dog;

var dog = new Dog('二狗', '哈士奇');

print(dog.feeling); // happy

原型链继承存在两个问题:第一点是当被继承对象中包含引用类型的属性时,该属性会被所有实例对象共享,示例代码如下;

function Animal() {

this.colors = ['red', 'green', 'blue'];

}

function Dog() {

}

// 继承Animal

Dog.prototype = new Animal();

Dog.prototype.constructor = Dog;

var dog1 = new Dog();

dog1.colors.push('black');

print(dog1.colors); // red,green,blue,black

var dog2 = new Dog();

print(dog2.colors); // red,green,blue,black

第二点是不能在不影响所有实例对象的情况下,向父级构造函数传递参数,这一点不做示例,大家可以自行验证下;

2、构造函数继承

function Dog(name, variety) {

Animal.apply(this, arguments);

this.name = name;

this.variety = variety;

}

var dog = new Dog('二狗', '哈士奇');

print(dog.feeling); // happy

这是一种十分简单的方法,使用apply或者call方法改变构造函数作用域,将父函数的构造函数绑定到子对象上。虽然解决了子对象向父对象传递参数的目的,但是借助构造函数,方法都在构造函数中定义,函数的复用就无从谈起。

3、构造函数和原型链组合继承

利用构造函数实现对实例属性的继承,使用原型链完成对原型属性和方法的继承,避免了原型链和构造函数的缺陷。

function Animal(name) {

this.name = name;

this.colors = ['red', 'green', 'blue'];

}

Animal.prototype.sayName = function() {

print(this.name);

};

function Dog(name, age) {

// 继承属性

Animal.call(this, name);

this.age = age;

}

// 继承方法

Dog.prototype = new Animal();

Dog.prototype.constructor = Dog;

Dog.prototype.sayAge = function() {

print(this.age);

}

var dog1 = new Dog('二狗', 1);

dog1.colors.push('black');

print(dog1.colors); // red,green,blue,black

dog1.sayName(); // 二狗

dog1.sayAge(); // 1

var dog2 = new Dog('二牛', 2);

print(dog2.colors); // red,green,blue

dog2.sayName(); // 二牛

dog2.sayAge(); // 2

4、YUI式继承

由原型链继承延伸而来,避免了实例对象的prototype指向同一个对象的缺点(Dog.prototype和Animal.prototype指向了同一个对象,那么任何对Dog.prototype的修改,都会反映到Animal.prototype)。让Dog跳过Animal,直接继承Animal.prototype,这样省去执行和创建Animal实例,提高了效率。利用一个空对象作为媒介,空对象几乎不占用内存,示例如下:

function Animal() {}

Animal.prototype.feeling = 'happy';

function extend(Child, Parent) {

var F = function(){};

F.prototype = Parent.prototype;

Child.prototype = new F();

Child.prototype.constructor = Child;

}

extend(Dog, Animal);

var dog = new Dog('二狗', '哈士奇');

print(dog.feeling); // happy

5、拷贝继承(浅拷贝和深拷贝)

把父对象的属性和方法,全部拷贝给子对象,也能实现继承。

① 浅复制

function Animal() {}

Animal.prototype.feeling = 'happy';

function extend(Child, Parent) {

var p = Parent.prototype;

var c = Child.prototype;

for (var i in p) {

c[i] = p[i];

}

}

extend(Dog, Animal);

var dog = new Dog('二狗', '哈士奇');

print(dog.feeling); // happy

但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能,比如在上例中适当位置添加如下代码会发现:

Animal.prototype.colors = ['red', 'green', 'blue'];

Dog.colors.push('black');

print(Dog.colors); // red,green,blue,black

print(Animal.colors); // red,green,blue,black

当然,这也是jquery早期实现继承的方式。

② 深复制

function Animal() {}

Animal.prototype.feeling = 'happy';

function deepCopy(Child, Parent) {

var p = Parent.prototype;

var c = Child.prototype;

for (var i in p) {

if (typeof p[i] === 'object') {

c[i] = (p[i].constructor === Array) ? [] : {};

deepCopy(p[i], c[i]);

} else {

c[i] = p[i];

}

}

}

deepCopy(Dog, Animal);

var dog = new Dog('二狗', '哈士奇');

print(dog.feeling); // happy

深拷贝,能够实现真正意义上的数组和对象的拷贝。这时,在子对象上修改属性(引用类型),就不会影响到父元素了。这也是目前jquery使用的继承方式。

JavaScript数据结构实现

可以下载javascript shell(进入该页面并滚动到底部,选择系统版本进行下载)使用shell交互模式编写代码并执行。博文中主要利用javascript中数组和对象的特性对数据结构和算法进行描述,在描述原理的同时,使用javascript实现示例代码。只有真正明白数据结构的基础,才能对其应用自如。

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 算法
    +关注

    关注

    23

    文章

    4599

    浏览量

    92643
  • 数据结构
    +关注

    关注

    3

    文章

    573

    浏览量

    40093

原文标题:为什么要使用数据结构和算法(程序=数据结构+算法)

文章出处:【微信号:TheAlgorithm,微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    DDC264配置寄存器数据写入和320 DCLK时钟脉冲后的回读数据结构是什么?

    配置寄存器数据写入和320 DCLK时钟脉冲后的回读数据结构是什么? 根据注和表9,16位配置寄存器数据,4位修订ID, 300位校验模式,怎么可能有1024 TOTAL READBACK BITS, format = 0
    发表于 11-19 07:58

    视觉软件HALCON的数据结构

    在研究机器视觉算法之前,我们需要先了解机器视觉应用中涉及的基本数据结构。Halcon数据结构主要有图像参数和控制参数两类参数。图像参数包括:image、region、XLD,控制参数包括:string、integer、real、
    的头像 发表于 11-14 10:20 171次阅读
    视觉软件HALCON的<b class='flag-5'>数据结构</b>

    架构师日记-从数据库发展历程到数据结构设计探析

    数据库发展史 起初,数据的管理方式是文件系统,数据存储在文件中,数据管理和维护都由程序员完成。后来发展出树形结构和网状
    的头像 发表于 09-25 11:20 751次阅读
    架构师日记-从<b class='flag-5'>数据</b>库发展历程到<b class='flag-5'>数据结构</b>设计探析

    嵌入式常用数据结构有哪些

    在嵌入式编程中,数据结构的选择和使用对于程序的性能、内存管理以及开发效率都具有重要影响。嵌入式系统由于资源受限(如处理器速度、内存大小等),因此对数据结构的选择和使用尤为关键。以下是嵌入式编程中常用的几种数据结构,结合具体特点和
    的头像 发表于 09-02 15:25 387次阅读

    神经网络算法结构有哪些类型

    神经网络算法是深度学习的基础,它们在许多领域都有广泛的应用,如图像识别、自然语言处理、语音识别等。神经网络的结构有很多种类型,每种类型都有其独特的特点和应用场景。以下是对神经网络算法结构
    的头像 发表于 07-03 09:50 387次阅读

    揭秘编程核心:基本数据结构算法思想详解

    描述问题的数据除了各数据元素本身,还要考虑各元素的逻辑关系,主要是一对一的线性关系,一对多的树型关系和多对多的图形关系。
    的头像 发表于 04-25 11:51 1038次阅读
    揭秘编程核心:基本<b class='flag-5'>数据结构</b>与<b class='flag-5'>算法</b>思想详解

    探索编程世界的七大数据结构

    结构就像是一颗倒挂的小树,有根、有枝、有叶。它是一种非线性的数据结构,以层级的方式存储数据,顶部是根节点,底部是叶节点。
    的头像 发表于 04-16 12:04 360次阅读

    TASKING编译器是否可以将数据结构设置为 \"打包\"?

    TASKING 编译器是否可以将数据结构设置为 \"打包\"? GCC 很早以前就提供了这种可能性,可以将__attribute__((packed))与对齐指令结合使用。 对于
    发表于 03-05 06:00

    矢量与栅格数据结构各有什么特征

    矢量数据结构和栅格数据结构是地理信息系统(GIS)中最常用的两种数据结构。它们在存储和表示地理要素上有着不同的方法和特征。在接下来的文章中,我们将详细介绍这两种数据结构并比较它们的特点
    的头像 发表于 02-25 15:06 2334次阅读

    区块链是什么样的数据结构组织

    区块链是一种特殊的数据结构,它以分布式、去中心化的方式组织和存储数据。区块链的核心原理是将数据分布在网络的各个节点上,通过密码学算法保证数据
    的头像 发表于 01-11 10:57 1988次阅读

    结构体与指针的关系

    在C语言中,结构体(Struct)是一种用户自定义的数据类型,它允许您将不同类型的数据项组合在一起,以便形成一个更复杂的数据结构结构体可以
    的头像 发表于 01-11 08:00 943次阅读
    <b class='flag-5'>结构</b>体与指针的关系

    C语言数据结构之跳表详解

    大家好,今天分享一篇C语言数据结构相关的文章--跳表。
    的头像 发表于 12-29 09:32 793次阅读
    C语言<b class='flag-5'>数据结构</b>之跳表详解

    redis数据结构的底层实现

    Redis是一种内存键值数据库,常用于缓存、消息队列、实时数据分析等场景。它的高性能得益于其精心设计的数据结构和底层实现。本文将详细介绍Redis常用的数据结构和它们的底层实现。 Re
    的头像 发表于 12-05 10:14 591次阅读

    数据结构:判断链表回文结构

    给定一个链表,判断该链表是否为回文结构。回文是指该字符串正序逆序完全一致。如当输入链表 {1,2,3,2,1} 时,断定是回文结构,输出True。
    的头像 发表于 12-01 13:26 614次阅读
    <b class='flag-5'>数据结构</b>:判断链表回文<b class='flag-5'>结构</b>

    不同数据结构的定义代码

    数据结构是相互之间存在一种或多种特定关系的数据元素的集合。
    的头像 发表于 11-29 14:13 626次阅读