数组与单、双链表详解

本文详细介绍了数组与链表的基本概念、定义方式、操作方法及其优缺点,并对比了两者的时间复杂度,帮助读者理解这两种常见数据结构的特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、数组

数组的定义

格式一:

元素类型[] 数组名 = new 元素类型[元素的个数或数组长度]

示例:

int[] array1 = new int[2];
array1[0]=1;
array1[1]=2;
array1[2]=3;

格式二:

元素类型[] 数组名 = new 元素类型[]{元素,元素,……};

示例:

int[] arr = new int[]{3,5,1,7};
int[] arr = {3,5,1,7};

注意:给数组分配空间时,必须指定数组能够存储的元素个数来确定数组大小。创建数组之后不能修改数组的大小。可以使用length属性获取数组的大小

数组的优缺点

优点

  • 随机访问性强(通过下标进行快速定位)
  • 查找速度快

    缺点

  • 插入和删除效率低(插入和删除需要移动数据)
  • 可能浪费内存(因为是连续的,所以每次申请数组之前必须规定数组的大小,如果大小不合理,则可能会浪费内存)
  • 内存空间要求高,必须有足够的连续内存空间。
  • 数组大小固定,不能动态拓展

二、链表

普及一下
线性表分为顺序存储(顺序表)和链式存储(单链表、双链表、循环链表、静态链表,前面三种指针实现,后面是借助数组实现)

1、单链表

1302135-20180721165735048-2117245400.png

单链表只有一个指向下一节点的指针,也就是只能next

定义单链表

首先定义一个Node类

 public class Node {
 protected Node next; //指针域  
 public  int data;//数据域  
   
 public Node( int data) {  
       this. data = data;  
 }  
   
 //显示此节点  
 public void display() {  
      System. out.print( data + " ");  
 }  
}

接下来定义一个单链表

public class LinkList {
public Node first; // 定义一个头结点
private int pos = 0;// 节点的位置

public LinkList() {
    this.first = null;
}

// 插入一个头节点
public void addFirstNode(int data) {
    Node node = new Node(data);
    node.next = first;
    first = node;
}

// 删除一个头结点,并返回头结点
public Node deleteFirstNode() {
    Node tempNode = first;
    first = tempNode.next;
    return tempNode;
}

// 在任意位置插入节点 在index的后面插入
public void add(int index, int data) {
    Node node = new Node(data);
    Node current = first;
    Node previous = first;
    while (pos != index) {
        previous = current;
        current = current.next;
        pos++;
    }
    node.next = current;
    previous.next = node;
    pos = 0;
}

// 删除任意位置的节点
public Node deleteByPos(int index) {
    Node current = first;
    Node previous = first;
    while (pos != index) {
        pos++;
        previous = current;
        current = current.next;
    }
    if (current == first) {
        first = first.next;
    } else {
        pos = 0;
        previous.next = current.next;
    }
    return current;
}

// 根据节点的data删除节点(仅仅删除第一个)
public Node deleteByData(int data) {
    Node current = first;
    Node previous = first; // 记住上一个节点
    while (current.data != data) {
        if (current.next == null) {
            return null;
        }
        previous = current;
        current = current.next;
    }
    if (current == first) {
        first = first.next;
    } else {
        previous.next = current.next;
    }
    return current;
}

// 显示出所有的节点信息
public void displayAllNodes() {
    Node current = first;
    while (current != null) {
        current.display();
        current = current.next;
    }
    System.out.println();
}

// 根据位置查找节点信息
public Node findByPos(int index) {
    Node current = first;
    if (pos != index) {
        current = current.next;
        pos++;
    }
    return current;
}

// 根据数据查找节点信息
public Node findByData(int data) {
    Node current = first;
    while (current.data != data) {
        if (current.next == null)
            return null;
        current = current.next;
    }
    return current;
}
}

测试类

public class TestLinkList {
public static void main(String[] args) {  
    LinkList linkList = new LinkList();  
    linkList.addFirstNode(20);  
    linkList.addFirstNode(21);  
    linkList.addFirstNode(19);  
     //print19,21,20  
    linkList.add(1, 22); //print19,22,21,20  
    linkList.add(2, 23); //print19,22,23,21,20  
    linkList.add(3, 99); //print19,22,23,99,21,20  
  //调用此方法会print 19,22,23,99,21,20 
    linkList.displayAllNodes();  
    }
}

2、双链表

1302135-20180721165813767-712683288.png

双链表除了有一个指向下一节点的指针外,还有一个指向前一结点的指针,可以通过prev快速找到前一结点

定义双链表

首先定义一个Node类

public class Node{
    public Node data;   //数据区
    public Node next;   //指针区
    public Node (Node data,Node next){
        this.data = data ;
        this.next = next;
    }
    public Node(){
    }
    public void setData(Node data){
        this.data = data;
    }
    public Node getData(){
        return data;
    }
    public void setNext(Node next){
        this.next=next;
    }
    public Node getNext(){
        return next;
    }
}

定义一个双链表

public class LinkList{
public  Node  head; //头节点
public  int  count;     //记录节点的长度
public  LinkList(){ //构造函数用来初始化
    head = null;
    count = 0;
}
//节点的添加
public void addNode(Node data){
    Node node = new Node(data,null);
    Node temp = null;
    If(head!= null){
        temp = head;
        while(temp.getNext()!=null){
            Temp = temp.getNext();
        }
    temp.setNext(node);
    }else{
        head = node;
        temp = node;
    }
count++;
}
//节点的删除
public void delNode(Node data){
    
        Node  front = null; //定义一个空节点,用于接收和判断被删除节点的前面还有没有节点
        while(head!= null){
            If(head.equals(data)){
                break;
            }
            front=head;
            head = head.getNext();
        }
        If(head!=null){
            If(front==null){
                head = head.getNext();
            }else{
                front.setNext(head.getNext());
            }
            count--;
        }else{
            Count--;
        }
    
}
//给定下标删除节点
public void delNode_count(int index){
    if(index<0||index>count-1){
        System.out.println("链表索引越界");
    }
    Node temp = head;   //作用同上
    //找到要删除节点的前一个节点
    for(int i=0;i<index-1;i++){
        temp=temp.getNext();
    }
                            //找到之后 此节点的前节点和此节点的后节点进行连接
                            //让要删除节点的前一个节点,指向被删除节点的后一个节点,也就是指向要删除节点的后后一个节点
    temp.setNext(temp.getNext().getNext());     //把要删除的节点隔过去进行连接,也就是实现了删除节点的操作
    //删除之后链表的长度变短了1位
    count--;
}
    //以给出的index 查找节点


public Node findNode(int index){
    if(index<0||index>count-1){
        System.out.println("链表索引越界");           
    }
    Node temp = head;
    for(int i=0;i<index-1;i++){
        temp=temp.getNext();    //找到之后获取index在链表中的位置,表示链表中第index个节点的值是temp.getData;
    }
    return temp;    //根据需要可返回找到的数据对象,也可不返回,此处建议返回,这样可以把链表封装起来
}

//以对象查找节点
public Node findNode(NodeData data){
        Node temp = head;
        while(temp!=null){
            if(temp.equals(data)){
                return temp;
            }
            temp.setNext(temp.getNext());
        }
        return null;
}
//修改
public void updateNode(NodeData data){
    Node temp = findNode(data);
    if(temp!=null){
        temp.setData(data);
    }
}
//打印
public void print(){
    Node temp=head;
    while(temp!=null){
        temp.print();
        //temp.print(temp.getData().toString());
        temp=temp.getNext();
    }
}
}

3、链表相对于数组的优缺点

时间复杂度对比

操作数组链表
读取O(1)O(n)
插入O(n)O(1)
删除O(n)O(1)

优点

  • 插入删除速度快(因为有next指针指向其下一个节点,通过改变指针的指向可以方便的增加删除元素)
  • 内存利用率高,不会浪费内存(可以使用内存中细小的不连续空间(大于node节点的大小),并且在需要空间的时候才创建空间)
  • 大小没有固定,拓展很灵活。

    缺点

  • 不能随机查找,必须从第一个开始遍历,查找效率低

4、单链表与双链表的区别

单链表只有一个指向下一结点的指针,也就是只能next

双链表除了有一个指向下一结点的指针外,还有一个指向前一结点的指针,可以通过prev()快速找到前一结点,顾名思义,单链表只能单向读取

双链表的优点

  • 删除单链表中的某个结点时,一定要得到待删除结点的前驱,得到该前驱有两种方法,第一种方法是在定位待删除结点的同时一路保存当前结点的前驱。第二种方法是在定位到待删除结点之后,重新从单链表表头开始来定位前驱。尽管通常会采用方法一。但其实这两种方法的效率是一样的,指针的总的移动操作都会有2*i次。而如果用双向链表,则不需要定位前驱结点。因此指针总的移动操作为i次。
  • 查找时也一样,我们可以借用二分法的思路,从中间节点开始前后同时查找,这样双链表的效率可以提高一倍。

单链表的优点

  • 从存储结构来看,每个双链表的节点要比单链表的节点多一个指针,而长度为n就需要 n*length(这个指针的length在32位系统中是4字节,在64位系统中是8个字节) 的空间,这在一些追求时间效率不高应用下并不适应,因为它占用空间大于单链表所占用的空间;这时设计者就会采用以时间换空间的做法,这时一种工程总体上的衡量。

转载于:https://www.cnblogs.com/sxkgeek/p/9347071.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值