链式存储结构之单链表

1.线性表的链式存储结构

线性表的链式存储结构允许数据元素存在任意未被占用的内存空间,因为在线性表的链式存储结构中,除了存储数据元素相关的数据信息之外,还存储了数据元素的后继元素存储地址,这样通过当前数据元素很容易找到下一个数据元素。

链式存储结构中,存取一个数据元素内容信息和直接后继的存储位置的结构称为结点,存储数据元素内容信息的域称为数据域,存储直接后继存储位置的域称为指针域。n个结点链接成一个链表,每个结点中只包含一个指针域的称为单链表。

2.单链表

2.1 结点

代码实现单链表,首先需要创建结点。我们知道结点中存储的是一个数据元素的数据信息和直接后继的内存地址(也就是下一个结点的内存地址),因此,可使用如下代码实现结点的创建。

class Node{
    int data;
    Node next;
    //直接使用数据信息初始化结点
    public Node(int data){
        this.data=data;
    }
    @Override
    public String toString() {
        return "Node [data=" + data + ", next=" + next + "]";
    }
}

实例化一个结点Node node=new Node(0); node.next指向的是后继结点,还是Node类型。

2.2 单链表的创建

在对单链表进行操作之前,比如存储,读取,插入,删除等,首先需要创建单链表。

当node的next指向的下一个元素不为null时,一直遍历,知道找到最后一个结点,即node.next=null的node,将创建的新结点添加到链表中。

代码实现:

//创建单链表
public static Node singleList(){
    Node head=node0;
    add(new Node(2));
    add(new Node(1));
    add(new Node(5));
    add(new Node(3));
    add(new Node(7));
    add(new Node(6));
    add(new Node(9));
    add(new Node(1));
    add(new Node(8));
    return head;
}

//向单链表中添加元素
public static void add(Node newNode){
    Node node=node0;
    while(node.next!=null){
        node=node.next;
    }
    node.next=newNode;
}

2.3 读取单链表的第i个数据元素

算法思路:
    1. 判断i是否合法,即i是否在链表的长度范围内;
    2. 声明一个指针temp指向链表的第一个结点,初始化j从1开始;
    3. 当j<i时,让temp指针向后移动,不断指向下一个结点,j累加1;
    4. 若查找成功,返回结点temp的数据。

代码实现:

//读取单链表的第i个数据
public static int findNode(int i){
    int iData=0;
    int length=calculateLength();
    System.out.println("length:"+length);
    if(i>length || i<1){
        System.out.println("i无效");
    }else{
        int j=1;
        while(j<i){
            temp=temp.next;
            j=j+1;
        }
        iData=temp.data;
    }
    return iData;
}

//计算单链表的长度
public static int calculateLength(){
    //temp1指向链表的第一个结点
    Node temp1=temp;
    int length=0;
    //计算单链表的长度
    //头结点放在第一元素之前,所以头结点不属于链表的长度计算范围
    while(temp1!=null){
        length=length+1;
        temp1=temp1.next;
    }
    return length;
}

读取算法的时间复杂度分析:该算法的时间复杂度取决于i的位置,当i为1时,不需要遍历,第一个就取出来了,当i为n时,则需遍历n-1次,因此最坏时间复杂度是O(n)。

2.4 单链表的第i个位置插入结点

算法思路:
    1. 声明一个指针temp2指向链表头结点,初始化j从1开始;
    2. 当j<i时,遍历链表,让temp2的指针向后移,指向下一个结点,且j累加1;
    3. 若到链表末尾,则说明第i个结点不存在;
    4. 否则查找成功,执行单链表的插入标准语句:node.next=temp.next; temp.next=node;

代码实现:

public static void insert(Node node,int i){
    Node temp2=temp;
    System.out.println(temp2);
    int j=1;
    while(j<i){
        temp2=temp2.next;
        j=j+1;
    }
    if(j>i){
        System.out.println("i超出线性表长度");
    }
    node.next=temp.next;
    temp.next=node;
}
    

2.5 单链表的第i个位置删除结点

算法思路:
    1. 声明一个指针temp2指向链表头结点,初始化j从1开始;
    2. 当j<i时,遍历链表,让temp2的指针向后移,指向下一个结点,且j累加1;
    3. 若到链表末尾,则说明第i个结点不存在;
    4. 否则查找成功,执行单链表的插入标准语句:node.next=node.next.next;

代码实现:

public static void delete(int i){
    Node temp3=temp;
    System.out.println(temp3);
    int j=1;
    while(j<i){
        temp=temp3.next;
        j=j+1;
    }
    if(j>i){
        System.out.println("删除位置不存在!");
    }
    temp3.next=temp3.next.next;
}

单链表插入和删除的算法时间复杂度分析:由上面的插入和删除操作,可以简单地将插入和删除操作分为两个步骤实现,第一步首先遍历i个结点,第二步就是在i位置插入或删除结点。可以看出算法的时间复杂度为O(n)。

之前分析过顺序存储结构的插入和删除算法时间复杂度为O(n),所以单链表在插入和删除操作上,与顺序存储结构的插入和删除是没有多大区别的。但是我们要在i的位置插入10个结点,对于顺序存储结构,每次都需要移动n-i个结点,每次都是O(n),而对于单链表,只需在第一次遍历得到i的指针,时间复杂度为O(n),之后的插入直接赋值指针即可实现,时间复杂度都是O(1)。所以单链表对于多个结点的插入和删除操作是很高效的。

完整的代码:

package com.java.ListInsert;
public class SingleList {
    static Node node0=new Node(0);
    //初始化temp指针指向第一个结点
    static Node temp=singleList().next;
    public static void main(String[] args){
        delete(1);
        int r=findNode(3);
        System.out.println(r);
    }
    
    //单链表的删除
    public static void delete(int i){
        Node temp3=temp;
        System.out.println(temp3);
        int j=1;
        while(j<i){
            temp=temp3.next;
            j=j+1;
        }
        if(j>i){
            System.out.println("删除位置不存在!");
        }
        temp3.next=temp3.next.next;
    }
    //单链表的插入
    public static void insert(Node node,int i){
        Node temp2=temp;
        System.out.println(temp2);
        int j=1;
        while(j<i){
            temp2=temp2.next;
            j=j+1;
        }
        if(j>i){
            System.out.println("i超出线性表长度");
        }
        node.next=temp.next;
        temp.next=node;
    }
    
    //读取单链表的第i个数据
    public static int findNode(int i){
        int iData=0;
        int length=calculateLength();
        System.out.println("length:"+length);
        if(i>length || i<1){
            System.out.println("i无效");
        }else{
            int j=1;
            while(j<i){
                temp=temp.next;
                j=j+1;
            }
            iData=temp.data;
        }
        return iData;
    }
    
    //计算单链表的长度
    public static int calculateLength(){
        //temp1指向链表的第一个结点
        Node temp1=temp;
        int length=0;
        //计算单链表的长度
        //头结点放在第一元素之前,所以头结点不属于链表的长度计算范围
        while(temp1!=null){
            length=length+1;
            temp1=temp1.next;
        }
        return length;
    }
    
    //创建单链表
    public static Node singleList(){
        Node head=node0;
        add(new Node(2));
        add(new Node(1));
        add(new Node(5));
        add(new Node(3));
        add(new Node(7));
        add(new Node(6));
        add(new Node(9));
        add(new Node(1));
        add(new Node(8));
        return head;
    }
    
    //向单链表中添加元素
    public static void add(Node newNode){
        Node node=node0;
        while(node.next!=null){
            node=node.next;
        }
        node.next=newNode;
    }
    
}
class Node{
    int data;
    Node next;
    //直接使用数据信息初始化结点
    public Node(int data){
        this.data=data;
    }
    @Override
    public String toString() {
        return "Node [data=" + data + ", next=" + next + "]";
    }
}

 

posted @ 2019-01-13 13:11  考拉熊_12  阅读(307)  评论(0编辑  收藏  举报