Q: 为什么要学习数据结构与算法?
A: 如果说Java语言是自动档轿车,C语言就是手动档吉普。数据结构呢?是变速箱的工作原理。你完全可以不知道变速箱怎样工作,就把自动档的车子从1档开到4档,而且未必就比懂得的人慢。写程序这件事,和开车一样,经验可以起到很大作用,但如果你不知道底层是怎么工作的,就永远只能开车,既不会修车,也不能造车。如果你对这两件事都不感兴趣也就罢了,数据结构懂得用就好。但若你此生在编程领域还有点更高的追求,数据结构是绕不开的课题。
Java 替你做了太多事情,那么多动不动还支持范型的容器类,加上垃圾收集,会让你觉得编程很容易。但你有没有想过,那些容器类是怎么来的,以及它存在的意义是什么?最粗浅的,比如 ArrayList 这个类,你想过它的存在是多么大的福利吗——一个可以随机访问、自动增加容量的数组,这种东西 C 是没有的,要自己实现。但是,具体怎么实现呢?如果你对这种问题感兴趣,那数据结构是一定要看的。甚至,面向对象编程范式本身,就是个数据结构问题:怎么才能把数据和操作数据的方法封装到一起,来造出 class / prototype 这种东西?
A: 此外,很重要的一点是,数据结构也是通向各种实用算法的基石,所以学习数据结构都是提升内力的事情。
链接:https://www.zhihu.com/question/20012022/answer/13688683
A: 这里是关于罗升阳的访谈,专访罗升阳:老罗的Android之旅
Q: 不同数据结构的优缺点?
数据结构 | 优点 | 缺点 |
---|---|---|
数组(Array) | 快速访问,如果知道下标,就可以非常快地存取 | 查找慢, 插入或删除慢, 大小固定 |
有序数组(OrderedArray) | 比无序的数组查找快 | 插入或删除慢,大小固定 |
栈(Stack) | 提供后进先出方式的存取 | 存取其他项很慢 |
队列(Queue) | 提供先进先出方式的存取 | 存取其他项很慢 |
链表(LinkedList) | 插入快,删除快 | 查找慢 |
二叉树(BinaryTree) | 查找、插入、删除都快(如果树保持平衡) | 删除算法复杂 |
红-黑树(RBTree) | 查找、插入、删除都快。树总是平衡的 | 算法复杂 |
2-3-4树(2-3-4Tree) | 查找、插入、删除都快。树总是平衡的。类似的树对磁盘存储有用 | 算法复杂 |
哈希表(HashTable) | 如果关键字已知则存取极快。插入快 | 删除慢,如果不知道关键字则存取很慢,对存储空间使用不充分 |
堆(Heap) | 插入、删除快,对最大数据项的存取很快 | 对其他数据项存取慢 |
图(Graph) | 对现实世界建模 | 有些算法慢且复杂 |
Q: 算法的概述?
A: 许多算法直接适用于上面的数据结构,都需要知道如何:
1) 插入一个新的数据项
2) 删除某一特定的数据项
3) 寻找某一个特定的数据项
4) 如何迭代地访问各个数据项
5) 排序
Q: 为什么面向对象编程比面向过程编程要好一些?
A: 面向过程编程,如C、Pascal或早期版本的Basic在处理大型的复杂问题时有些力不从心,为什么会这样呢?有两类问题:一是程序与现实世界缺乏相应关系;二是程序内部的结构出现了问题。
A: 对现实世界建模的无能为力
使用过程语言对现实世界问题进行抽象及概念化十分困难:方法执行任务,而数据存储信息,但是现实世界中的事物是对两者同时进行操作的。
例如,炉子上的自动调温器例子,如果用过程语言来写这个程序,可能会以两个函数turnOn()和turnOff()即炉开和炉闭来完成,但是还会有两个全局变量currentTemp和desiredTemp,即现在的温度和希望的温度。
然而这些函数和变量并没有形成任何编程对象,在程序中不会出现任何可以称之为自动调温器的单元,这个单元的唯一概念仅存在程序员的脑海中。
对于大型的程序,有可能包括上百个类似调温器的实体,面向过程语言对这种情况将会使程序变得极为混乱,错误频繁出现,有时甚至不可能实现方案,因此需要一种可以更好地将程序中的事物与现实中的事物相匹配的语言。
A: 粗糙的组织结构
解决程序的内部组织是一个更微妙而且事关重大的问题。面向对象被划分为一个一个的函数,这种基于函数组织形式的一个巨大问题是它仅仅考虑了函数,而没有重视数据。当不得不面对数据时,它没有过多的选择。简而言之,数据可以是一个特定的函数的局部变量,也可以使所有函数都可以存取的全局变量,就是无法规定一个变量只允许某些函数存取而不允许另一些函数存取。
如果一个变量要想被多个函数存取到,就必须为全局变量,但是全局变量会在不经意间被程序的任何一个函数存取,这就导致了频繁的程序错误。
Q: 什么是软件工程?
A: 软件工程研究是许多程序员参与的大型复杂的计算机程序的创建方法。它强调是程序的整体设计和如何依照最终用户需求而进行设计的问题。
A: 软件工程关系一个软件项目的整个生命周期,包括分析、设计、验证、编码、测试、生产和维护各个阶段。
Q: C++和Java的几处不同?
A: 这里只列出可以帮助C++程序员看懂示例的Java特性
A: 没有指针
Java和C++的最大区别在于Java没有指针,其实,Java只是摆脱了显示表露的指针,指针依旧是以存储地址的形式隐藏在程序的深处,有时甚至可以说在Java中,所有的东西都是指针。
1) 引用(reference)
Java对基本数据类型的处理与对象的处理不同
int nVar; // an int variable called nVar
BankAccount bankAccount; // reference to a BankAccount object
在第一行语句中,一个被称为nVar的存储地址保存了一个数值127,然而,bankAccount这个存储地址并没有保存BankAccount对象的数据,这个名称bankAccount是对象的引用,它并不是对象本身。
实际上,如果bankAccount没有被预先赋值的话,它就不会称为引用。在赋值为某个对象之前,它保存了一个被称为null的特殊对象的引用。同样,nVar在它被赋值之前也不会保存数值。如果尝试使用一个被赋值的变量,编译器会报错。
在C++中
BankAccount bankAccount;
实际上在栈里创建了一个对象,它留出了所有这个对象的数据的空间。而在Java中,这条语句只创建了一个放置某一对象的存储地址的空间。可以将Java引用认为是普通变量语法中的指针。
2) 赋值
Java中的赋值操作符与C++中的不一样。
在C++中,这条语句:
bankAccount2 = bankAccount1;
将一个名为bankAccount1的对象的所有数据都拷贝到另一个名为bankAccount2的对象中。然而在Java中,这条语句的赋值语是向bankAccount2拷贝了bankAccount1指向的存储地址,现在bankAccount1和bankAccount2实际指的是同一个对象,它们都是这个对象的引用。
如果对赋值操作符理解不深的话,上面的语句会让人感到困惑。
如果想对一个对象中复制数据到另一个对象中,必须保证在一开始就有两个不同的对象,然后分别拷贝每个字段,等号是不起复制的作用的。
3) new操作符
在Java中任何创建对象的工作必须使用new。但是new在Java中返回一个引用,而不像C++中返回一个指针。下面是创建对象的一个方法:
BankAccount bankAccount = null;
bankAccount = new BankAccount();
取消指针意味着一个更安全的系统。程序员不可能找到bankAccount的真实地址,也就不可能意外地破坏它。
用new向堆申请空间后,如何去释放那些不再使用的空间?在C++中,可以使用delete。在Java中,则根本不需要对释放空间担心。Java每隔一段时间就会查看每一块由new开辟的内存,看指向它的有效引用是否依旧存在,如果这个引用不存在,系统会自动将这块空间归入空闲内存区。这个过程被称为垃圾回收。
几乎所有的程序员在使用C++过程中都会有忘记删除存储空间的经历,这会导致“内存泄漏”(memory leak)从而消耗系统资源,使程序处于不稳定的状态,甚至会导致系统崩溃。内存泄漏在Java中不可能出现(或至少几乎没有)。
4) 参数
在C++中,指针经常被用来在函数间传递对象,从而避免拷贝一个大对象的系统开销。在Java中,对象经常以引用的形式传递,这种方法同样地避免了对象的拷贝:
void f1() {
BankAccount bankAccount = new BankAccount(270.00);
f2(bankAccount);
}
void f2(BankAccount ba) {}
上面的代码中引用bankAccount和ba都指向同一个对象,而在C++中,ba则是从bankAccount拷贝而来的另一个对象。
然而,简单的基本数据类型总是通过它的值来传递。
5) 相等与同一
在Java中,对基本数据类型,可以通过相等操作符(==)来判断两个变量是否含有相同的值:
int a = 12;
int b = a;
if (a == b) {
System.out.println("they are equal");
}
这同C/C++语法。
但是在Java中,由于关系操作符使用引用,所以它们在涉及到对象的判断时有些不同。 当使用相等操作符(==)判断类时,实际上判断的是类的引用是否一致,即它们是否指向的是同一个对象:
BankAccount ba1 = new BankAccount(250.00);
BankAccount ba2 = ba1;
if (ba1 == ba2) {
System.out.println("they are identical");
}
在C++中,这个操作符会判断两个对象是否含有相同的数据。如果在Java中要判断两个对象是否含有相同的数据,则使用Object类的equals()方法:
BankAccount ba1 = new BankAccount(250.00);
BankAccount ba2 = new BankAccount(250.00);
if (ba1 == ba2) {
System.out.println("they are equal");
}
A: 重载操作符
Java中没有重载操作符,在C++中可以重新定义+、*、=及大多数其他操作符,以便使它们在特定的类中达到不同的效果。在Java中,任何类似的重新定义都是不可能的,而可以使用命名的方法,如add()或其他名字。
A: 基本数据类型
Java八种基本数据类型
参考
- 《Java数据结构和算法》Robert Lafore 著,第1章 - 综述