数据结构:顺序表+链表
一。顺序表:
首先在了解顺序表和链表之前,先了解一下线性表,**线性表(linear list)**是n个具有相同特征元素的有限序列 ,在逻辑上是线性结构,也就是一条连续的直线,但是在物理上不一定是连续的。常见的线性表:顺序表,链表,栈,队列…
顺序表是用一段物理地址连续的存储单元一次储存数据元素的线性结构,一般情况下使用数组存储。在数组上完成数据的增删查改
下面将了解顺序表的底层实现逻辑:
接口:
public interface SeqList { public void add(int data);//新增元素,默认在数组最后进行新增 public void add(int pos,int data);//新增元素,在pos这个位置加上data这个数据 public boolean contain(int toFind);//查看toFind这个元素是否在数组中存在 public int index(int toFind);//查看这个元素在数组中的下标 public int get(int pos);//获取pos位置的元素 public void set(int pos,int value);//给pos位置的值修改为value public int remove(int toRemove);//删除第一次出现的的关键字key public int size();//获取顺序表的长度 public void display();//打印顺序表 }
接口的实现:
import java.util.Arrays; public class Main implements SeqList{ private int[] elem=new int[10]; private int usedSize; @Override public void add(int data) { if(isFull()){ elem= Arrays.copyOf(elem,elem.length*2); } this.elem[usedSize]=data; usedSize++; } public boolean isFull(){ if(usedSize==elem.length){ return true; } return false; } @Override public void add(int pos, int data) { if(pos<0 || pos>usedSize){ System.out.println("输入不合法");//可以把这个写进一个方法中,然后写一个异常,如果pos不合法就抛出异常 } if(isFull()){ elem=Arrays.copyOf(elem,elem.length*2); } for(int i=usedSize-1;i >=pos;i--){//先把所有的元素向后移动一个单元,当i<pos的时候就结束 elem[i+1]=elem[i]; } elem[pos]=data; usedSize++; } @Override public boolean contain(int toFind) { for(int i=0;i<usedSize;i++){ if(elem[i]==toFind){ return true; } } return false; } @Override public int index(int toFind) { if(this.contain(toFind)){ for(int i=0;i<usedSize;i++){ if(elem[i] == toFind){ return i; } } } return 0; } @Override public int get(int pos) { if(pos<0||pos>usedSize-1){ System.out.println("输入的元素不合法"); }else { for (int i=0;i<usedSize;i++){ if(pos==i){ //System.out.println(elem[pos]); return elem[pos]; } } } return 0; } @Override public void set(int pos, int value) { if(pos<0||pos>usedSize){ System.out.println("输入不合法"); } elem[pos]=value; } @Override public void remove(int toRemove) { int ret=this.index(toRemove);//获取删除元素的下标 for(int i=ret;i<usedSize-1;i++){ elem[i]=elem[i+1]; } elem[usedSize-1]=0; usedSize--; } @Override public int size() { int count=0; for(int i=0;i<elem.length;i++){ count++; } return count; } @Override public void display() { for(int i=0;i<usedSize;i++){ System.out.println(elem[i]+" "); } } }
二。ArrayList的使用
ArrayList是以泛型方式实现的,使用时必须先要将其实例化
1.ArrayList的构造:
List<Integer> list1=new ArrayList<>();//构造一个空的列表 List<Integer> list1=new ArrayList<>(10);//构造一个列表,其中含有10个元素
2.ArrayList的常见操作;
import java.util.ArrayList; public class Main { public static void main(String[] args) { ArrayList<Integer> list1=new ArrayList<>(); list1.add(10);//加入元素 list1.add(0,10)//在0下标的位置插入数字10 list1.remove(1);//删除下标为1的值 list1.get(2);//获取下标为2的值 list1.set(1,3);//把下标为1的位置的值改为3 list1.contain(12);//是否有12这个值在此线性表中 list1.indexOf(10);//返回第一个值为10的下标 list1.size();//获取整个顺序表的元素个数 } }
注意:
当实例一个空的列表时,第一次add默认分配一个大小为10的内存(在实例化阶段不分配内存)
扩容是自动以1.5倍的形式扩容
3.顺序表的遍历
//法一: for(int i=0;i<list.size();i++){ System.out.println(list.get(i)); } //法二: System.out.println(list);
三。ArrayList的具体使用例子
杨辉三角的实现:
上述这个图片就是我们常说的杨辉三角,在高中的时候没少接触这个东西
这个杨辉三角主要的实现方式是通过二维顺序表实现的,下面将对用二维顺序表来实现杨辉三角的实现
import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class Soulation { public static void main(String[] args) { Scanner sc=new Scanner(System.in); System.out.println("请输入"); int input=sc.nextInt(); List<List<Integer>> list=new ArrayList<>(); //第一行的导入 List<Integer> arr=new ArrayList<>(); arr.add(1); list.add(arr); //从第二行开始,进行计算、 for(int i=1;i<input;i++){ List<Integer> curRow=new ArrayList<>(); curRow.add(1); List<Integer> prevRow=list.get(i-1); for(int j=1;j<i;j++){ int val=prevRow.get(j)+prevRow.get(j-1); } curRow.add(1); list.add(curRow); } } }
四。链表:
在介绍链表之前,先说一下ArrayList的缺点;当在ArrayList任意位置进行删除元素或者增加元素的时候,就需要将所有元素进行前移或者后移,这样时间复杂度就是O(n),效率非常低,因此涉及到数据的大量插入和删除的操作就不太适合ArrayList了,因此引入了链表来解决这个问题
链表是一种物理存储上非连续的存储结构,数据元素的逻辑顺序是通过链表中的引用链接的次序进行实现的(物理上是不连续的,但是逻辑上是连续的)
图示:
五。无头单向链表的实现
接口:
public interface SingleLinkedList { public void add(int data);//头插法 public void addLast(int data);//尾插法 public void addIndex(int index,int data);//把data插入到index位置 public boolean contains(int key);//链表中是否存在数据key public void remove(int key);//删除第一次出现key数据的节点 public void display();//展示链表中所有的元素 }
接口的实现:
public class SingleList implements SingleLinkedList{ static class ListNode{ public int val; public ListNode next; public ListNode(int val){ this.val=val; } } public ListNode head; @Override public void add(int data) { ListNode node=new ListNode(data); if(this.head==null){ head=node; }else { node.next=head; head=node; } } @Override public void addLast(int data) { ListNode node=new ListNode(data); ListNode cur=head; if(head==null){ head=node; }else{ while(cur.next!=null){ cur=cur.next; } cur.next=node; } } @Override public void addIndex(int index, int data) { ListNode node=new ListNode(data); int count=0; ListNode cur=head; if(head==null){ head=node; }else{ while(cur.next!=null){ if(count==index-1){ ListNode hi=cur.next; cur.next=node; node.next=hi; break; } count++; } } } @Override public boolean contains(int key) { ListNode cur=head; if(head==null){ return false; }else{ while(cur.next!=null){ if(cur.val==key){ return true; } cur=cur.next; } } return false; } @Override public void remove(int key) { ListNode cur=head; ListNode last=head; if(head.val==key){ head=head.next; }//当要删除的值是链表的第一位的时候 while(cur.next!=null){ cur=cur.next; if(cur.val==key){ last.next=cur.next; } last=last.next; } } @Override public void display() { ListNode cur=head; while(cur!=null){ System.out.print(cur.val+" "); cur=cur.next; } } }
主函数:
public class Main { public static void main(String[] args) { SingleList singleList=new SingleList(); singleList.addLast(12); singleList.addLast(23); singleList.addLast(34); singleList.addLast(45); singleList.addIndex(1,2); singleList.display(); System.out.println(singleList.contains(2)); singleList.remove(23); singleList.display(); } }
以上就是单链表的增删查所有的代码,大家可以尝试自己写一遍
六。LinkedList的使用
1.LinkedList介绍:
LinkedList本质上是一个双向链表,由于链表没有将元素存储在连续的空间之中,元素存储在单独的节点之中,然后通过引用节点将节点连接起来了,因此在插入或删除元素的时候,不需要搬移元素,效率较高
2.LinkedList的构造:
List<Integer> list1=new LinkedList<>();
3.LinkedList的其他方法的介绍:
list1.add(45);//尾插45 list1.add(3,10);//在3这个位置插入10这个数字 list1.remove(2);//删除2位置这个元素 list1.get(2);//获取下标为2的元素的值 list1.set(2,199);//把下标为2的位置的值改为199 list1.contains(199);//查看此链表中是否含有199这个数字 list1.indexOf(199);//返回这个链表中第一次出现199这个元素的下标
4.LinkedList的遍历:
法一:
System.out.println(list);
法二:
for(int i=0;i<list.size;i++){ System.out.println(list.get(i)); }
七。ArrayList与LinkedList的区别
不同点 | ArrayList | LinkedList |
---|---|---|
存储空间上 | 物理上连续 | 逻辑上连续,但是物理上不一定连续 |
随机访问 | 支持 | 不支持 |
头插 | 需要搬移元素,效率低,O(n) | 只用修改引用的指向,空间复杂读:O(1) |
插入 | 空间不够时可以进行扩容 | 没有容量大概念 |
应用场景 | 元素高效存储+频繁访问 | 任意位置删除添加频繁 |