我是野猪。
想在开始介绍之前,说下自己一步步开始自学数据结构与算法的思想,首先为啥要学?通常情况下,精心选择合适的数据结构可以带来更高的运行或存储效率;其次如何去学?我认为刚开始学习数据结构(或者说算法)类似于射箭,“射艺无其他窍门儿,手熟是第一秘诀,你多练几次,自然能领悟其中道理”,又或者说像是学骑自行车,刚开始都比较吃力,只要坚持不懈的学、多多练习自然能像一个常人那样骑车子,至于以后的进阶才有的人骑得花样百出、有的人造诣更深。
数据结构是计算机存储、组织数据的方式,也就是说数据结构不单单是数据存储的容器,还要有对数据的基本操作,如检索、插入、删除、展示等。通常情况下,精心选择合适的数据结构可以带来更高的运行或存储效率。数据结构通常与高效的检索算法和索引技术有关。
以数组为例,是为了处理方便,把具有相同类型的若干变量按有序的形式组织起来,这些按顺序排列的同类型数据的结合称为数组,并且还有对其中数据的基本操作。
代码如下:
/**
* 有序数组
*/
public class OrderArray {
//创建int类型的数组
private final int[] intArray;
//创建的数组默认的长度
private int aLength = 0;
public OrderArray(int max) {
intArray = new int[max];
}
/**
* 插入元素
*
* @param elem
*/
public void insertElem(int elem) {
int location = 0;
//1. 插入新的元素时 要先找到元素所在的位置 因为是有序数组 遍历只要找到坐标所在的元素大于插入元素则停止
for (; location < aLength; location++) {
if (intArray[location] > elem) {
break;
}
}
//2. 所在位置之后的元素全部后移一位
for (int i = aLength; location < i; i--) {
intArray[i] = intArray[i - 1];
}
//3. 插入元素
intArray[location] = elem;
//4. 长度加1
aLength++;
}
/**
* 遍历查看所有元素
*/
public void showAllElem() {
for (int i = 0; i < aLength; i++) {
System.out.println(intArray[i] + "\t");
}
}
/**
* 二分法查找某元素是否在有序数组中,有则返回所在的下标,否则返回-1
*
* @param val
* @return
*/
public int searchElem(int val) {
int low = 0;
int high = aLength - 1;
while (true) {
int mid = (low + high) / 2;
if (intArray[mid] == val) {
return mid;
} else if (low == mid) {
//只有当切割完之后的数组中的元素个数是1或2时 才会满足
if (val == intArray[high]) {
return high;
} else {
return -1;
}
} else {
if (intArray[mid] < val) {
//说明查询的值val在下标mid和high之间 因此low = mid
low = mid;
} else if (intArray[mid] > val) {
high = mid;
}
}
}
}
/**
* 删除数据
*
* @param val
* @return
*/
public boolean deleteElem(int val) {
int index = -1;
//1. 先检索val值所在的下标
int tempLocation = searchElem(val);
//2.确定下标后其后的元素全部前移一位
if (tempLocation != -1) {
for (int i = tempLocation; i < aLength-1; i++) {
intArray[i] = intArray[i + 1];
}
//3.数组长度减一
aLength--;
return true;
} else {
System.out.println("要删除的元素不存在,请重新输入");
return false;
}
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
OrderArray orderArray = new OrderArray(5);
orderArray.insertElem(1);
orderArray.insertElem(4);
orderArray.insertElem(3);
orderArray.insertElem(5);
orderArray.insertElem(10);
orderArray.showAllElem();
int i = orderArray.searchElem(0);
System.out.println("所查元素的下标是"+i);
boolean deleteElem = orderArray.deleteElem(5);
if (deleteElem) {
orderArray.showAllElem();
}
}
}
对数据的处理有插入数据、展示全部数据、检索数据、删除数据等。
插入数据:首先遍历整个数据集,当插入的元素大于某下标对应的值则break跳出循环,拿到当前的location,其后的元素全部后移一位。
展示数据:遍历整个数据集即可。
检索数据:用的是典型针对有序数据集的二分查找(又叫折半查找)法,使得查询效率大大提高(文章末尾有详细分析并引入一些基本的数据结构概念)。
删除数据:删除数据是在检索数据的基础上,找到指定元素的位置下标,下标之后的元素全部前移一位。
二分查找:可以看出循环中的每一步将查找的范围缩小一半,最终这个范围会缩小到无法再分割。基本图示如下:
通过二分查找我们可以体会这样一件事即当我们查找一个有序数组的时候可以先切割一半比较一直找到对应的元素,假设数据量是100,查询过程体现在数字上就是下面这张图:
这就是时间复杂度的概念:浅显地理解就是当你数据范围很大时,花费的时间变化如何。
假设我们在上述数组例子中查找具体值然后返回下标的过程中不是采用二分查找而是整个数据集循环遍历,那么最多花费的次数是数据集的长度,比如100个数,花费的时间最长就是100,而二分只是7;范围扩大到1000时,花费的时间是1000,而二分只是10,所以说整个查询效率有很大的区别尤其是数据量很大时。
下图是常见的增长数量级的总计:接下来我们会一直用到。
比如对数基本的二分查找的时间复杂度就称为O(log N)。
通过以上以数组为例我们简单的说了一些数据结构的基本概念,方便我们入门。
最后补充一些上述没有提到的最基本的概念:
数据的逻辑结构:指反映数据元素之间的的逻辑关系。
1.线性结构:数据元素之间是一对一的关系。
2.树形结构:数据元素之间是一对多的关系。
3.图形结构:数据元素之间是多对多的关系。
常见的数据结构有:
数组 :为了处理方便,把具有相同类型的若干变量按有序的形式组织起来。这些按顺序排列的同类型数据的结合称为数组。
栈 :只能在一端插入和删除的特殊线性表。按照先进后出的原则存储数据。
队列 :一种特殊的线性表,原则是只允许在表的前端进行删除操作,而在表的后端进行插入操作。显然队列是按照先进先出的原则组织数据。
链表 :链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑结构是通过链表中的指针链接次序实现的。链表由一系列的结点(链表中的每一个元素称为结点)组成,每个结点都分为两部分:一个是存储数据元素的数据域;另一个是存储下一个结点地址的指针域。
树 :暂时理解不好,稍后补充,哈哈。
图 :暂时理解不好,稍后补充,哈哈。
堆 :堆是一种特殊的树形结构,每个结点都有一个值。通常我们所说的堆的数据结构,是指二叉堆。堆的特点是根结点的值最大(或最小),且根结点的两个子树也是一个堆。