首先要知道运行时间的长短和占用内存空间的大小,是衡量一个程序好坏的重要因素。
时间复杂度
时间复杂度中的时间指的是语句的执行次数,而不是实际耗费的时间。
如何知道一个程序的时间复杂度呢?有如几个原则。
1.如果运行时间是常数量级,则用常数1表示。
2.只保留时间函数中的最高阶项。
3.如果最高阶项存在,则省去最高阶项前面的系数。
例如:
下面是几种常见的关系:
常见的时间复杂度按照从低到高的顺序,包括O(1)、O(logn)、O(n)、 O(nlogn)、O(n 2)等。
空间复杂度
在时间复杂度相同的情况下,算法占用的内存空间越小越好,空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度。
常见的空间复杂度按照从低到高的顺序,包括O(1)、O(n)、O(n 2)等。其中递 归算法的空间复杂度和递归深度成正比。
数组
数组在空间中是连续存储的,下标是从0开始的。
读取元素
1. int[] array = new int[]{3,1,2,5,4,9,7,2};
2. // 输出数组中下标为3的元素
3. System.out.println(array[3]);
更新元素
1. int[] array = new int[]{3,1,2,5,4,9,7,2};
2. // 给数组下标为5的元素赋值
3. array[5] = 10;
4. // 输出数组中下标为5的元素
5. System.out.println(array[5]);
插入元素
尾部插入
把要插入的元素放在数组的最后就行,类似更新元素。
中间插入
由于数组是连续的,如果在中间插入,就需要把插入位置的元素及其后面的元素向后面移动。
超范围插入
如果一个数组的长度为6,里面的元素已经满了,这时要想插入该怎么办呢?
这就涉及到数组的扩容了,其实很简单,只需要创建一个新的数组,数组长度是原来的两倍,再把原来的数组内容复制到新的数组中,这时再插入你要插入的元素就可以了。
删除元素
删除操作和插入操作正好相反,如果删除的元素位于中间,则后面的元素都要向前移动一位
写代码注意
如果插入元素在数组尾部,则传入元素的下标就等于数组的长度,如果传入的元素在中间或头部,则传入元素的下标就小于数组的长度,如果传入元素的下标大于数组的长度或小于0就会抛出异常。
数组的优势和劣势
优势:
有很高效的随机访问能力,只要给出下标就能找到对应元素。
劣势:
数组的中间插入和删除都会导致后面大量的元素移动,影响效率。
所以数组适合读操作多,写操作少的场景。
链表
链表不是连续的,链表的一个节点包含两个部分,分别是数据域和指针域,最后一个节点的指针指向空,链表只要知道一个节点的指针,就可以知道他后面所有的元素。双向链表只是多了一个前指针(prev),头结点的指针也指向空。
查找和更新节点
只要知道头结点,头节点的指针指向下一个节点,下一个节点的指针又指向下下一个节点,以此类推,就可以找到要查找的节点;更新节点只需要把该节点的数据域更新即可。
插入节点
尾部插入
只需要让原来尾部节点的指针指向要插入的即可。
头部插入
两步:
1.让新节点的指针指向原来的头结点。
2.再把新节点变为头结点即可。
中间插入
两步:
1.新节点的指针指向指向插入位置的节点。
2.插入位置前的节点指针指向新节点即可。
删除节点
尾部删除
只需要倒数第二个节点的指针指向空即可。
头部删除
把链表的头结点设置为原先头结点的next指针即可。
中间删除
把要删除节点的前置节点的指针指向要删除节点的下一个节点即可。
链表的优势
能够灵活的进行删除和插入操作
从表格可以看出,数组可以更好的查找元素,链表可一个更好的删除和插入元素。
小结
数组是由有限个相同类型的变量所组成的有序集合,他的物理存储方式为顺序存储,访问方式是随机访问。
链表由若干节点组成,每个节点包含指向下一节点的指针;链表的物理存储方式为随机存储,访问方式是顺序访问。
Ending:
OK,本篇文章就到此结束了,非常感谢你能看到这里,所以如果你觉得这篇文章对你有帮助的话,请点一个大大的赞,支持一下博主,若你觉得有什么问题或疑问,欢迎私信博主或在评论区指出~