线性表
线性表在计算机中可以用顺序存储和链式存储两种存储结构来表示。
其中用顺序存储结构存储的叫做顺序表。
用链式存储结构表示的叫做链表。
顺序存储
地址连续
预先分配内存,可能会导致浪费
查改容易,直接通过下标就可以访问
增删不方便,每一次增加或者删除,后面的所有数据元素需要向前移动一位或者向后移动一位
代码实现
public static void main(String[] args) { //需要提前分配好空间 int array[]=new int[10]; for (int i = 0; i <array.length ; i++) { array[i]=i; } //查找元素 System.out.println(array[2]); //修改元素 array[2]=666; //删除一个元素,后面的所有元素需要向前移动一位 for (int i = 2; i <9 ; i++) { array[i]=array[i+1]; } }
链式存储
可以动态增长长度
增删容易,不用将后面的数据元素进行移动,只需修改指针就行了。
查改不方便,需要从头开始遍历进行查找
几种链表
单向链表,每一个节点存放一个指针,只指向后一个节点或前一个节点
双向链表,每一个节点存放两个指针,一个指向前一个节点,一个指向后一个节点
循环链表,尾节点存放一个指针,指向首指针,形成回路
代码实现(单向链表)
public class Demo { public static void main(String[] args) throws Exception { LinkList myList=new LinkList(); for (int i = 0; i < 10; i++) { myList.insert(i,i); } myList.display(); System.out.println(myList.length()); myList.remove(1); System.out.println(myList.get(1)); System.out.println(myList.indexOf(1)); } }class Node{ public Object data; public Node next; public Node(Object data) { this.data=data; } public Node() {} }class LinkList{ public Node head; public LinkList() { head=new Node(); } //清空链表 public void clear() { head.next=null; head.data=null; } //判断是否为空 public boolean isEmpty() { return head.next==null; } //获得链表长度 public int length() { Node p=head.next; int length=0; while(p!=null) { length++; p=p.next; } return length; } //按位序号查找 public Object get(int i)throws Exception { Node p=head.next; int j=0; while(p!=null&&j<i) { p=p.next; j++; } if(j>i||p==null) { throw new Exception("第"+i+"个元素不存在"); } return p.data; } //按值查找 public int indexOf(Object x) { Node p=head.next; int j=0; while(p!=null&&!p.data.equals(x)) { p=p.next; j++; } if(p==null) return -1; else return j; } //插入操作 public void insert(int i,Object x)throws Exception { Node p=head; int j=-1; while(p!=null&&j<i-1) { p=p.next; j++; } if(j>i-1||p==null) { throw new Exception("第"+i+"个元素不存在"); } else { Node newNode=new Node(x); newNode.next=p.next; p.next=newNode; } } //删除操作 public void remove(int i)throws Exception { Node p=head; int j=-1; while(p.next!=null&&j<i-1) { p=p.next; j++; } if(j>i-1||p.next==null) throw new Exception("删除位置不合法"); p.next=p.next.next; } //输出所有节点 public void display() { Node node=head.next; while(node!=null) { System.out.println(node.data); node=node.next; } } }
运行结果
输出所有 0 1 2 3 4 5 6 7 8 9 链表长度为:10 删除第1号元素 输出所有 0 2 3 4 5 6 7 8 9 输出第1号元素 2 获取1的位置-1
顺序存储vs链式存储
没有哪一种数据结构更好,只有哪一种数据结构更适合某一种场景
对于查找较为频繁的宜用顺序表,因为他的查找时间复杂度为O(1)
对于增加删除较为频繁的宜用链式表,因为如果用顺序表,插入或者删除的时候后面的数据元素需要全部移动
空间上,顺序表需要提前分配好空间,这个时候可能浪费空间也可能空间不够,相对来说,链表更加灵活。
应用(经典的约瑟夫问题)
设有编号为1,2,……,n的n(n>0)个人围成一个圈,从第1个人开始报数,报到m时停止报数,杀掉报m的人,再从他的下一个人起重新报数,报到m时停止报数,报m杀掉,……,如此下去,直到最后剩下k个人为止。
用数组来做
import java.util.Scanner;public class 约瑟夫 { static int people,deleteNum,aliveNum; public static void f(int array[],int index,int nowPeople) { for(int i=0;i<people;i++) { //如果当前人数等于最后要求剩下的人数 跳出循环 if(nowPeople==aliveNum) return; //如果不为0表示该人未被杀掉 if(array[i]!=0) { //如果数到的数是7 if(index%deleteNum==0) { //标识为0 即杀掉 array[i]=0; //重新报数 index=1; //现存人数减一 nowPeople--; } //如果当前报的数未被杀死 继续报数 else index++; } } f(array,index,nowPeople); } public static void main(String[] args) { getData(); kill(); } /** * kill */ public static void kill() { int array[]=new int [people]; for(int n=0;n<people;n++) { array[n]=n+1; } f(array,1,people); for(int n:array) { if(n!=0) System.out.println(n); } } /** * 获取用户输入的数据 */ public static void getData() { Scanner in=new Scanner(System.in); System.out.println("输入总人数"); people=in.nextInt(); System.out.println("数到第几个人删除"); deleteNum=in.nextInt(); System.out.println("最后剩下几个人"); aliveNum=in.nextInt(); } }
用双向链表
import java.util.Scanner;public class 约瑟夫环2 { static int people,deleteNum,aliveNum; public static void main(String[] args) { getData(); Node n=buildLink(); int index=1; int nowPeople=people; while(true) { //如果当前人数为最后要求剩下的人数 跳出 if(nowPeople==aliveNum) break; //如果报到的数为前一位 即如果第七个数的人杀掉 那么报到6的时候,开始杀人 if(index==deleteNum-1) { n.next=n.next.next; n=n.next; //重新报数 index=1; //当前人数减一 nowPeople--; } //如果不是 继续报数 else { n=n.next; index++; } } print(n); } // TODO 自动生成的方法存根 /** * 获取用户输入的数据 */ public static void getData() { Scanner in=new Scanner(System.in); System.out.println("输入总人数"); people=in.nextInt(); System.out.println("数到第几个人删除"); deleteNum=in.nextInt(); System.out.println("最后剩下几个人"); aliveNum=in.nextInt(); } /** * 创建链表 * @return */ public static Node buildLink() { Node head=new Node(1); for (int i = 2; i < people+1; i++) { head.addNode(new Node(i)); } head.addNode(head); return head; } /** * 打印链表 * @param head */ public static void print(Node head) { Node currentNode=head; while(true) { System.out.print((Integer)currentNode.inner+" "); currentNode=currentNode.next; if(currentNode==head) break; } System.out.println(); } }
两种方式对比
经过对比,可以发现,双向链表更容易理解,而且逻辑也更简单,所以说,选择一个正确的数据结构去解决问题是非常重要的。
我会讲完数据结构的大部分内容,下一篇将会讲讲栈和队列,哈哈,觉得对你有帮助的话,可以继续关注。
我觉得分享是一种精神,分享是我的乐趣所在,不是说我觉得我讲得一定是对的,我讲得可能很多是不对的,但是我希望我讲的东西是我人生的体验和思考,是给很多人反思,也许给你一秒钟、半秒钟,哪怕说一句话有点道理,引发自己内心的感触,这就是我最大的价值。(这是我喜欢的一句话,也是我写博客的初衷)
http://www.cnblogs.com/-new/p/7127892.html