跟着阿姨走,幸福啥都有——关阿姨笔录
零 不同算法时间复杂度对比
package 数据结构.Day01;
import java.util.Scanner;
public class Recursion {
public static void main(String[] args) {
long n=0;
System.out.print("请输入要求的第几个的数:");
Scanner scanner=new Scanner(System.in);
if(scanner.hasNextLong()) {
n= scanner.nextLong();
}
scanner.close();
System.out.println("循环实现:第"+n+"个斐波那契数为:"+fun2(n));
System.out.println("递归实现:第"+n+"个斐波那契数为:"+fun1(n));
}
public static long fun1(long n ) {
if(n<=1) return n;
return fun1(n-1)+fun1(n-2);
}
public static long fun2(long n) {
if(n<=1) return n;
long first=0;
long second=1;
for(int i=1;i<=n-1;i++) {
long sum=first+second;
first=second;
second=sum;
}
return second;
}
}
package 数据结构.Day01;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TimeTool {
private static final SimpleDateFormat fmt=new SimpleDateFormat("HH:mm:ss.SSS");
public interface Task {
void execute();
}
public static void check(String title,Task task){
if(task==null){
return;
}
title=(title==null)?"":("["+title+"]");
System.out.println(title);
System.out.println("开始:"+fmt.format(new Date()));
long begin=System.currentTimeMillis();
task.execute();
long end=System.currentTimeMillis();
System.out.println("结束:"+fmt.format(new Date()));
double mill=(end-begin)/1000.0;
System.out.println("耗时:"+mill+"秒");
System.out.println("------------");
}
}
package 数据结构.Day01;
public class Test01 {
public static void main(String[] args) {
int n=46;
TimeTool.check("fun1", new TimeTool.Task() {
@Override
public void execute() {
System.out.println(Recursion.fun1(n));
}
});
TimeTool.check("fun2", new TimeTool.Task() {
@Override
public void execute() {
System.out.println(Recursion.fun2(n));
}
});
}
}
- 总结
- 递归的时间复杂度是O(2^n),而循环的时间复杂度是O(n)
一 线性表(ArrayList)
- 线性表时最基本、最简单、也是最常用的一种数据结构、一个线性表是n个具有相同特征的数据元素的有序列表。
- 数组是一种顺序存储的线性表。
- 存储多个值,每个元素可以通过索引访问。
- 数据按照顺序存储在连续位置的存储器中。
- 优点:
- 缺点:
- 插入和删除比较慢,比如:插入或者删除一个元素时,整个表需要遍历移动元素来重新排一次顺序
- 不可以增长长度,有空间限制,当需要存取的元素个数可能多于顺序表的元素个数时,会出现“溢出”问题当元素个数远少于预先分配的空间时,空间浪费
- 动态数组接口设计
int size():/元素的数量
boolean isEmpty()://是否为空
boolean contains(Eelement)://是否包含某个元素
void add(E element)://添加元素到最后面
E get(int index)://返回index位置对应的元素
E set(int index,Eelement)://设置index位置的元素
void add(int index,E element)://往index位置添加元素
E remove(int index)://删除index位置对应的元素
int indexOf(E element)://查看元素的位置
void clear()://清除所有元素
1.1 线性表的增删改查实现
1.1.1 查找
public int indexOf(E element) {
if(element==null) {
for(int i=0;i<size;i++) {
if(elements[i]==null) {
return i;
}
}
}else {
for(int i=0;i<size;i++) {
if(element.equals(elements[i])) {
return i;
}
}
}
return ELEMENT_NOT_FOUND;
}
1.1.2 添加
- 图解
public void ensureCapacity(int capacity) {
if(elements.length>capacity) {
return ;
}
E[] newElements = (E[])new Object[elements.length + (elements.length >> 1)];
for(int i=0;i<size;i++) {
newElements[i]=elements[i];
}
elements=newElements;
}
public void add(int index,E element ) {
if(index<0 || index>size) {
throw new ArrayIndexOutOfBoundsException("下标越界,允许范围:size 0=>"+size+"当前索引为:"+index);
}
ensureCapacity(size);
for(int i=size;i>index;i--) {
elements[i]=elements[i-1];
}
elements[index]=element;
size++;
}
1.1.3 删除
public E remove(int index) {
checkIndex(index);
E old=elements[index];
for(int i=index;i<size;i++) {
elements[i]=elements[i+1];
}
size--;
elements[size]=null;
if(size<=elements.length>>1) {
System.out.println("开始缩容");
ensureCapacity(elements.length>>1);
}
return old;
}
1.2 综合代码
1.2.1 接口
package 数据结构.Day03;
public interface List<E> {
int size();
int indexOf(E element);
boolean contains(E element);
E get(int index);
E set(int index,E element);
void clear();
void add(E element);
void add(int index,E element);
E remove(int index);
boolean isEmpty();
}
1.2.2 公共抽象类
package 数据结构.Day03;
public abstract class AbstractList<E> implements List<E> {
protected int size=0;
protected static final int ELEMENT_NOT_FOUND=-1;
@Override
public int size() {
return size;
}
public boolean contains(E element) {
return indexOf(element)!=ELEMENT_NOT_FOUND;
}
@Override
public void add(E element) {
add(size,element);
}
public boolean isEmpty() {
return size==0;
}
public void checkIndex(int index) {
if(index<0 || index>=size) {
throw new ArrayIndexOutOfBoundsException("下标越界,允许范围:size 0=>"+(size-1)+"当前索引为:"+index);
}
}
public void checkAddIndex(int index) {
if(index<0 || index>size) {
throw new ArrayIndexOutOfBoundsException("下标越界,允许范围:size 0=>"+size+"当前索引为:"+index);
}
}
}
1.2.3 实现的代码
package 数据结构.Day02;
import 数据结构.Day03.AbstractList;
public class DynamicArray<E> extends AbstractList<E> {
private static final int DEFAULT_CAPACITY=10;
private E[] elements=(E[]) new Object[DEFAULT_CAPACITY];
public DynamicArray() {
this(DEFAULT_CAPACITY);
}
public DynamicArray(int capacity) {
if(capacity<DEFAULT_CAPACITY) {
elements=(E[]) new Object[DEFAULT_CAPACITY];
} else {
elements=(E[]) new Object[capacity];
}
}
public int indexOf(E element) {
if(element==null) {
for(int i=0;i<size;i++) {
if(elements[i]==null) {
return i;
}
}
}else {
for(int i=0;i<size;i++) {
if(element.equals(elements[i])) {
return i;
}
}
}
return ELEMENT_NOT_FOUND;
}
public void checkIndex(int index) {
if(index<0 || index>=size) {
throw new ArrayIndexOutOfBoundsException("下标越界,允许范围:size 0=>"+(size-1)+"当前索引为:"+index);
}
}
public E get(int index) {
checkIndex(index);
return elements[index];
}
public E set(int index,E element) {
return elements[index];
}
public void clear() {
for(int i=0;i<size;i++) {
elements[i]=null;
}
}
public void add(E element) {
add(size,element);
}
public void ensureCapacity(int capacity) {
if(elements.length>capacity) {
return ;
}
E[] newElements = (E[])new Object[elements.length + (elements.length >> 1)];
for(int i=0;i<size;i++) {
newElements[i]=elements[i];
}
elements=newElements;
}
public void add(int index,E element ) {
if(index<0 || index>size) {
throw new ArrayIndexOutOfBoundsException("下标越界,允许范围:size 0=>"+size+"当前索引为:"+index);
}
ensureCapacity(size);
for(int i=size;i>index;i--) {
elements[i]=elements[i-1];
}
elements[index]=element;
size++;
}
public E remove(int index) {
checkIndex(index);
E old=elements[index];
for(int i=index;i<size;i++) {
elements[i]=elements[i+1];
}
size--;
elements[size]=null;
if(size<=elements.length>>1) {
System.out.println("开始缩容");
ensureCapacity(elements.length>>1);
}
return old;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("size:" + size + " => [");
for(int i=0;i<size;i++) {
if(i!=0) {
sb.append(" ,");
}
sb.append(elements[i]);
}
sb.append("]");
return sb.toString();
}
}
1.2.4 测试代码
package 数据结构.Day02;
public class Test02 {
public static void main(String[] args) {
DynamicArray list = new DynamicArray(10);
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
list.add(7);
list.add(8);
list.add(9);
list.add(10);
System.out.println(list.toString());
System.out.println(list.size());
list.add(11);
list.add(12);
System.out.println(list.toString());
list.remove(2);
list.remove(2);
list.remove(2);
list.remove(2);
System.out.println(list.toString());
list.remove(2);
System.out.println(list.toString());
}
}
1.3 练习题
- 将数组中的0放在最后面,其他元素按照原来的顺序排列
public static void test01() {
int[] nums={0,1,0,3,12};
int head=0;
int tail=0;
for(tail=0;tail<nums.length;tail++) {
if(nums[tail]!=0) {
if(head!=tail) {
nums[head]=nums[tail];
nums[tail]=0;
}
head++;
}
}
for(int num:nums) {
System.out.print(num+" ");
}
}
public static void test02() {
int[] nums01={0,1,0,3,12};
int head=0;
int tail=0;
int temp=0;
for(tail=0;tail<nums01.length;tail++) {
if(nums01[tail]!=0) {
temp=nums01[head];
nums01[head]=nums01[tail];
nums01[tail]=temp;
head++;
}
}
for(int num:nums01) {
System.out.print(num+" ");
}
}
二 链表(LinkedList)
- 链表是一种链式存储的线性表,所有元素的内存地址不一定是连续的
- 链表的优缺点
- 优点:可动态增加删除,大小可变
- 缺点:只能通过顺次指针访问,查询效率低
- 常见链表的分类
2.1 单向链表的实现
2.1.1 查询节点的值
- 思路图解
- 代码实现
public void checkIndex(int index) {
if(index<0 || index>=size) {
throw new ArrayIndexOutOfBoundsException("下标越界,允许范围:size 0=>"+(size-1)+"当前索引为:"+index);
}
}
@Override
public E get(int index) {
checkIndex(index);
return node(index).element;
}
private Node<E> node(int index) {
checkIndex(index);
Node<E> node=first;
for(int i=0;i<index;i++) {
node = node.next;
}
return node;
}
2.1.2 添加节点
- 思路图解【情况分为头节点添加和非头节点添加】
- 代码实现
public void checkAddIndex(int index) {
if(index<0 || index>size) {
throw new ArrayIndexOutOfBoundsException("下标越界,允许范围:size 0=>"+size+"当前索引为:"+index);
}
}
@Override
public void add(int index, E element) {
checkAddIndex(index);
if(index==0) {
first = new Node<>(first, element);
}else {
Node<E> pre = node(index - 1);
Node<E> next = pre.next;
pre.next=new Node(next,element);
}
size++;
}
2.1.3 删除节点
- 思路图解【情况分为头节点添加和非头节点添加】
- 代码实现
@Override
public E remove(int index) {
checkIndex(index);
Node<E> oldNode=first;
if(index==0) {
first=first.next;
}else {
Node<E> pre = node(index - 1);
oldNode = pre.next;
pre.next=pre.next.next;
}
size--;
return oldNode.element;
}
2.2 综合代码
2.2.3 实现的代码
package 数据结构.Day03;
public class LinkedList<E> extends AbstractList<E> {
private Node<E> first;
private class Node<E> {
E element;
Node<E> next;
public Node( Node<E> next,E element) {
this.element = element;
this.next = next;
}
}
@Override
public E get(int index) {
checkIndex(index);
return node(index).element;
}
private Node<E> node(int index) {
checkIndex(index);
Node<E> node=first;
for(int i=0;i<index;i++) {
node = node.next;
}
return node;
}
@Override
public E set(int index,E element) {
checkIndex(index);
Node<E> node = node(index);
E old = node.element;
node.element=element;
return old;
}
@Override
public void clear() {
size=0;
first=null;
}
@Override
public int indexOf(E element) {
Node<E> node=first;
if(element==null) {
for(int i=0;i<size;i++) {
if(node.element==null) {
return i;
}
node=node.next;
}
}else {
for(int i=0;i<size;i++) {
if(element.equals(node.element)) {
return i;
}
node=node.next;
}
}
return ELEMENT_NOT_FOUND;
}
@Override
public void add(int index, E element) {
checkAddIndex(index);
if(index==0) {
first = new Node<>(first, element);
}else {
Node<E> pre = node(index - 1);
Node<E> next = pre.next;
pre.next=new Node(next,element);
}
size++;
}
@Override
public E remove(int index) {
checkIndex(index);
Node<E> oldNode=first;
if(index==0) {
first=first.next;
}else {
Node<E> pre = node(index - 1);
oldNode = pre.next;
pre.next=pre.next.next;
}
size--;
return oldNode.element;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("size:" + size + " => [");
Node<E> node=first;
for(int i=0;i<size;i++) {
if(i!=0) {
sb.append(" ,");
}
sb.append(node.element);
node=node.next;
}
sb.append("]");
return sb.toString();
}
}
2.2.4 测试
package 数据结构.Day03;
public class Test {
public static void main(String[] args) {
List Linked = new LinkedList<>();
Linked.add(111);
Linked.add(222);
System.out.println(Linked.toString());
Linked.add(0, 0);
System.out.println(Linked.toString());
Linked.remove(0);
System.out.println(Linked.toString());
}
}
2.3 双向链表的实现
2.3.1 增加新节点
链表中间位置
链表开始位置
链表末尾位置
空链表的添加
2.3.2 删除节点
链表中间位置
链表头位置【包括只一个节点的情况】
2.4 综合代码
package 数据结构.Day04;
import 数据结构.Day03.AbstractList;
public class LinkedListDouble<E> extends AbstractList<E> {
private Node<E> first;
private Node<E> last;
private class Node<E> {
E element;
Node<E> pre;
Node<E> next;
public Node(Node<E> pre, Node<E> next, E element) {
this.element = element;
this.pre = pre;
this.next = next;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if(pre!=null) {
sb.append(pre.element);
}else {
sb.append("null");
}
sb.append("<-").append(element).append("->");
if(next!=null) {
sb.append(next.element);
}else {
sb.append("null");
}
return sb.toString();
}
}
@Override
public E get(int index) {
checkIndex(index);
return node(index).element;
}
private Node<E> node(int index) {
checkIndex(index);
if(index< (size>>1)) {
Node<E> node=first;
for(int i=0;i<index;i++) {
node = node.next;
}
return node;
}else {
Node<E> node=last;
for(int i=size-1;i>index;i--) {
node=node.pre;
}
return node;
}
}
@Override
public E set(int index,E element) {
checkIndex(index);
Node<E> node = node(index);
E old = node.element;
node.element=element;
return old;
}
@Override
public void clear() {
size=0;
first=null;
last=null;
}
@Override
public int indexOf(E element) {
Node<E> node=first;
if(element==null) {
for(int i=0;i<size;i++) {
if(node.element==null) {
return i;
}
node=node.next;
}
}else {
for(int i=0;i<size;i++) {
if(element.equals(node.element)) {
return i;
}
node=node.next;
}
}
return ELEMENT_NOT_FOUND;
}
@Override
public void add(int index, E element) {
checkAddIndex(index);
if(index==size) {
Node<E> oldLast=last;
last=new Node<E>(oldLast,null,element);
if(oldLast==null) {
first=last;
}else {
oldLast.next=last;
}
}else {
Node<E> next = node(index);
Node<E> pre = next.pre;
Node<E> node = new Node<E>(pre, next, element);
next.pre=node;
if(pre==null) {
first = node;
}else {
pre.next=node;
}
}
size++;
}
@Override
public E remove(int index) {
checkIndex(index);
Node<E> node = node(index);
Node<E> pre = node.pre;
Node<E> next = node.next;
if(pre==null) {
first=next;
}else {
pre.next=next;
}
if(next==null) {
last=pre;
}else {
next.pre=pre;
}
size--;
return node.element;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("size:" + size + " => [");
Node<E> node=first;
for(int i=0;i<size;i++) {
if(i!=0) {
sb.append(" ,");
}
sb.append(node.toString());
node=node.next;
}
sb.append("]");
return sb.toString();
}
}
2.4.1 测试
public static void main(String[] args) {
System.out.println("双向链表:");
LinkedListDouble<Object> doubleList = new LinkedListDouble<>();
doubleList.add(1);
doubleList.add(2);
doubleList.add(3);
System.out.println(doubleList);
doubleList.add(0,4);
System.out.println(doubleList);
doubleList.remove(3);
System.out.println(doubleList);
}
2.5 总结
三 双向链表和动态数组的对比
- 双向链表VS动态数组
- 动态数组:开辟、销段内存空间的次数相对较少,但是可能造成空间的浪费(通过缩容解决)
- 双向链表:开辟、销毁内存空间的次数相对较多,但不会造成空间的浪费
- 如果需要频繁在末尾添加删除操作,动态数组和双向链表均可
- 如果频繁需要在头部讲行增删操作,建议使用双向链表
- 如果在任意位置增删元素建议使用双向链表
- 如果频繁查询(随机访问元素)建议使用动态数组
|