线性表
- 线性表在计算机中可以用顺序存储和链式存储两种存储结构来表示。
- 其中用顺序存储结构存储的叫做顺序表。
- 用链式存储结构表示的叫做链表。
顺序存储
- 地址连续
- 预先分配内存,可能会导致浪费
- 查改容易,直接通过下标就可以访问
- 增删不方便,每一次增加或者删除,后面的所有数据元素需要向前移动一位或者向后移动一位
代码实现
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;
if(array[i]!=0)
{
if(index%deleteNum==0)
{
array[i]=0;
index=1;
nowPeople--;
}
else
index++;
}
}
f(array,index,nowPeople);
}
public static void main(String[] args)
{
getData();
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;
if(index==deleteNum-1)
{
n.next=n.next.next;
n=n.next;
index=1;
nowPeople--;
}
else
{
n=n.next;
index++;
}
}
print(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();
}
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;
}
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();
}
}
两种方式对比
- 经过对比,可以发现,双向链表更容易理解,而且逻辑也更简单,所以说,选择一个正确的数据结构去解决问题是非常重要的。
我会讲完数据结构的大部分内容,下一篇将会讲讲栈和队列,哈哈,觉得对你有帮助的话,可以继续关注。
我觉得分享是一种精神,分享是我的乐趣所在,不是说我觉得我讲得一定是对的,我讲得可能很多是不对的,但是我希望我讲的东西是我人生的体验和思考,是给很多人反思,也许给你一秒钟、半秒钟,哪怕说一句话有点道理,引发自己内心的感触,这就是我最大的价值。(这是我喜欢的一句话,也是我写博客的初衷)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· .NET 进程 stackoverflow异常后,还可以接收 TCP 连接请求吗?
· 本地部署 DeepSeek:小白也能轻松搞定!
· 如何给本地部署的DeepSeek投喂数据,让他更懂你
· 在缓慢中沉淀,在挑战中重生!2024个人总结!
· 大人,时代变了! 赶快把自有业务的本地AI“模型”训练起来!
· 从 Windows Forms 到微服务的经验教训