<数据结构基础学习>(四)链表

哎喂,别急着看啊,我还没有编辑好就误点发布了,今天晚上开完会回去就整理好,第二天回来看啊!

一.链表

动态数组、栈、队列底层都是依托静态数组实现的,靠resize来解决固定容量问题。

链表是真正的动态数据结构,是一种最简单的一种动态数据结构。

更深入的理解引用(或者指针)。

更深入的理解递归。

辅助成其他数据结构。

二。链表 Linked list

数据存储在“节点”(Node)中

class Node{

E e;

 Node next;

}

最后一个节点nxet=null

优点:真正的动态,不需要处理固定容量的问题。

缺点:丧失了随机访问的能力(即给出索引直接得到索引位置的元素)

数组最好用于索引有语意的情况

最大的优点:支持快速查询

链表不适合用于索引有语音的情况

最大的优点:动态

三.链表的方法实现

新建类LinkedList<E>

1.为了对用户屏蔽底层实现,在LinkedList中建立内部类Node。

在内部类中设置关于Node的构造方法

public class LinkedList<E> {    //节点设置为内部类,链表结构内可以访问Node,用户外部不可访问
    //对用户屏蔽底层实现
    private class Node{
        public E e;
        public Node next;

        public Node(E e, Node next){
            this.e = e;
            this.next = next;
        }

        public Node(E e){
            this(e, null);
        }

        public Node(){
            this(null,null);
        }

        @Override
        public String toString() {
            return e.toString();
        }
    }

2.成员变量与基本方法

链表头要存储为head

数组尾部添加元素是非常方便的(size指向最后一个元素的下一位)

在链表头添加元素是非常方便的(head跟踪链表头)

基本的成员变量为head和size

  //基本成员变量
    private Node head;
    private int size;

基本方法

    //  构造函数
    public LinkedList(){
        head = null;
        size = 0;
    }

    //获取链表中元素个数
    public int getSize(){
        return size;
    }

    //返回链表是否为空
    public boolean isEmpty(){
        return size == 0;
    

2.向表头添加元素

a.设置插入元素为node

b.node的next指向head,即node.next = head

c.让head = node,即head = node

其实可以化为一句:head = new Node(e, head);

d.最后维护size,size ++

 //在链表头添加新的元素e
    public void addFirst(E e){
        Node node = new Node(e);
        node.next = head;
        head = node;
        // head = new Node(e, head);
        size ++;
    }

3.链表中间添加元素

a.对index进行判断,若index < 0 || index>size,则抛出异常index不合法。若index = 0,则直接调用addFirst()方法在表头添加元素

b.创建插入节点node

c.从head开始循环遍历寻找插入节点索引的前一个节点prev

node.next = prev.next;

prev.next = node;   顺序不能颠倒

化为一句话:prev.next=new Node(e, prev.next);

d.最后维护size,size ++

    //在链表index(0-based)位置添加新的元素
    //在链表中不是一个常用操作
    public void add(int index, E e){
        if(index < 0 || index > size){
            throw new IllegalArgumentException("Add failed. Illagal index.");
        }

        if(index == 0){
            addFirst(e);
        }else
            //找到待插入节点的前一个节点
            {
            Node prev = head;
            for(int i = 0 ; i < index - 1 ; i ++){
                prev = prev.next;
            }
            Node node = new Node(e);
            node.next = prev.next;
            prev.next = node;
            //prev.next = new Node(e, prev.next);
            size ++;
        }

    }

4.链表末尾添加元素,直接调用add()方法。

    //链表末尾添加元素e
    public void addLast(E e){
        add(size, e);
    }

5.常用技巧:为链表设立虚拟头节点

首节点元素 为dummyHead.next

需要对基本成员变量和构造函数进行修改

  //基本成员变量
    private Node dummyhead;
    private int size;

    //  构造函数
    public LinkedList(){
        dummyhead = new Node(null,null);
        size = 0;
    }

对添加方法进行修改,循环遍历次数由index-1变为index,因为加了虚拟头节点,多了一位。

   //在链表index(0-based)为值添加新的元素
    //在链表中不是一个常用操作
    public void add(int index, E e){
        if(index < 0 || index > size){
            throw new IllegalArgumentException("Add failed. Illagal index.");
        }
        Node prev = dummyhead;
        for(int i = 0 ; i < index  ; i ++){
           prev = prev.next;
        }
           Node node = new Node(e);
           node.next = prev.next;
           prev.next = node;
           //prev.next = new Node(e,next);
           size ++;

    }

但此时不用对index是否为0进行判断,addFirst()方法也可以优化为

   //在链表头添加新的元素e
    public void addFirst(E e){
        add(0,e);
    }

6.链表的查询操作

当找index位置前一个位置的节点,从dummyhead开始遍历

当找index位置的节点,从dummyhead.next开始遍历

获得链表index位置的元素

a.先判断index是否合法

b.令Node cur = dummyhead.next,开始遍历index次,循环体中令cur=cur.next

c.返回cur.e

//获得链表index(0-based)位置的元素
    //在链表中不是一个常用操作
    public E get(int index){
        if(index < 0 || index >= size){
            throw new IllegalArgumentException("Get failed. Illegal index");
        }

        Node cur = dummyhead.next;
        for( int i = 0 ; i < index ; i ++){
            cur = cur.next;
        }
        return cur.e;

    }

由此得到getFirst()方法和getLast()方法,getLast()方法中为get(size - 1)

  //获得链表的第一个元素
    public E getFirst(){
        return get(0);
    }

    //获得链表最后一位元素
    public E getLast(){
        return get(size - 1);
    }

7.修改链表中index位置的方法set(int index, E e)

a.先判断index是否合法

b.令Node cur = dummyhead.next,开始遍历index次,循环体中令cur=cur.next

c.令cur.e = e

 //修改链表的第index(0-based)位置的元素为e
    //在链表中不是一个常用的操作
    public void set(int index, E e){
        if(index < 0 || index >= size){
            throw new IllegalArgumentException("Get failed. Illegal index");
        }

        Node cur = dummyhead.next;
        for( int i = 0 ; i < index ; i ++){
            cur = cur.next;
        }
        cur.e = e;
    }

8.查找是否存在元素e

新的遍历形式

  //查找是否存在元素e
    public boolean contains(E e){
        Node cur = dummyhead.next;
        while(cur == null){
            if(cur.e.equals(e)){
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

9.重写toString()方法

    @Override
    public String toString() {

        StringBuilder res = new StringBuilder();
//        Node cur = dummyhead.next;
//        while(cur != null){
//            res.append(cur+"->");
//            cur = cur.next;
//        }
        for(Node cur = dummyhead.next ; cur != null ; cur = cur.next){
            res.append(cur + "—>");
        }
        res.append("NULL");
        return res.toString();
    }

现在对于遍历整个链表有两种形式

a.

       Node cur = dummyhead.next;
      while(cur != null){
            res.append(cur+"->");
            cur = cur.next;
        }

b.

for(Node cur = dummyhead.next ; cur != null ; cur = cur.next){
            res.append(cur + "—>");
        }

10.链表中元素的删除

有虚拟头节点的链表

删除“索引”为index位置的元素delNode

prev.next = delNode.next;

delNode.next = null;

11.时间复杂度分析

添加操作

addLast(e)  O(n)

addFirst(e)  O(1)  与数组相反

add(index, e)   O(n/2) = O(n)

删除操作

removeLast(e)  O(n)

removeFirst(e)  O(n)

remove(index, e)  O(n/2) = O(n)

修改操作

set(index, e)  O(n)

查找操作 O(n)

get(index)   O(n)

contains(e)   O(n)

增、删、改、查均为O(n)

对链表头进行增加,删除,查询时时间复杂度为O(1)。

12.使用链表实现栈

LinkedListStack<E> implements Stack<E>

public class LinkedListStack<E> implements Stack<E> {

    private LinkedList<E> list;

    public LinkedListStack(){
        list = new LinkedList<>();
    }

    @Override
    public int getSize() {
        return list.getSize();
    }

    @Override
    public boolean isEmpty() {
        return list.isEmpty();
    }

    @Override
    public void push(E e) {
        list.addFirst(e);
    }

    @Override
    public E pop() {
        return list.removeFirst();
    }

    @Override
    public E peek() {
        return list.getFirst();
    }

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append("Stack: top");
        res.append(list);
        return res.toString();
    }
}

13.使用链表实现队列

原文地址:https://www.cnblogs.com/HarSong13/p/10672065.html

时间: 2024-10-18 11:35:54

<数据结构基础学习>(四)链表的相关文章

学习java数据结构基础知识之链表

public class Link { public int iData; public double dData; public Link next; public Link(int iData, double dData) { super(); this.iData = iData; this.dData = dData; } public void displayLink(){ System.out.print("{"+iData+","+dData +&qu

数据结构基础(8) --单链表的设计与实现(1)之基本操作

链表简介 数组的缺点: 1.元素插入:除了在数组的末尾插入元素之外,在数组的其他任何位置插入元素都需要进行数组元素的频繁移动(插入位置之后的元素都需往后移动), 时间复杂度约为O(N); 2.数组的删除:除了在数组的末尾删除元素之外,在数组的其他任何位置删除元素都需要进行数组元素的频繁移动(删除位置之后的元素都需往前移动), 时间复杂度也为O(N); 链表的特点: 由于在链表中插入/删除元素都不需要进行数据的移位, 只需要O(1)时间完成, 因此链表适用于频繁插入与删除的情况; 但是链表也有缺点

linux基础学习四

本次继续学习linux基础命令,包括stat.touch.cp.mv.rm.tree.mkdir.rmdir stat 命令格式 stat [OPTION]... FILE... 命令功能 显示文件的时间戳,即访问时间.修改时间和改变时间 stat [OPTION]... FILE...      [[email protected] ~]# stat newfile       File: `newfile'      Size: 0          Blocks: 0          I

Spring基础学习(四)&mdash;AOP

一.AOP基础 1.基本需求      需求: 日志功能,在程序执行期间记录发生的活动. ArithmeticCalculate.java public interface ArithmeticCalculate{ public int add(int a,int b); public int sub(int a,int b); public int mul(int a,int b); public int div(int a,int b); } ArithmeticCalculateImpl.

数据结构基础(9) --单链表的设计与实现(2)之高级操作

链表的链接: 将第二条链表的所有内容链接到第一条链表之后, 其完整实现代码与解析如下: //链表的链接 template <typename Type> void MyList<Type>::concatenate(const MyList<Type> &list) { if (isEmpty())//如果自己的链表为空 { first = list.first; return ; } else if (list.isEmpty()) //如果第二条链表为空 {

数据结构基础(10) --单链表迭代器的设计与实现

为了向 STL 致敬(O(∩_∩)O~), 我们模仿STL中的list的迭代器, 我们也自己实现一个MyList的迭代器, 以供遍历整个链表的所有元素: 首先:Node节点需要做如下修改(注意后缀有+的代码) //链表节点 template <typename Type> class Node { friend class MyList<Type>; friend class ListIterator<Type>; //+ template <typename T

Hibernate基础学习(四)&mdash;对象-关系映射(上)

一.映射对象标识符      Java语言按内存地址来识别或区分同一个类的不同对象,而关系数据库按主键值来识别或区分同一个表的不同记录.Hibernate使用对象标识符(OID)来建立内存中的对象和数据库表中的记录的对应关系,对象的OID和数据库表的主键对应,为了保证OID的唯一性和不可变性,应该让Hibernate,而不是应用程序来为OID赋值.     Hibernate推荐在数据表中使用代理主键,即不具备业务含义的字段.代理主键通常为整型,因为整型比字符串要节省更多数据库空间.     在

CSS基础学习四:元素选择器

在上一篇的博客中我们已经熟悉CSS语法,对于CSS代码格式也有了一些认识.下面我就来说一下CSS代码式: 选择器名称 { 属性名:属性值:属性名:属性值:......} 属性与属性之间用分号隔开:属性与属性值直接使用冒号连接:如果一个属性有多个属性值的话,那么多个属性值用空格隔开. 上述提到了我这一篇博客所要说的主题,就是选择器.选择器就是指定CSS语法要作用的标签,那个标签的名称 就是选择器. 基本的选择器可分为三种: a)html标签名选择器:使用的是html的标签名,又叫元素选择器. b)

Mybatis基础学习(四)&mdash;关系映射

一.模型分析 user和orders user---->orders 一个用户可以创建多个订单,一对多. orders--->user 一个订单只由一个用户创建,一对一.   orders和orderdetail orders--->orderdetail 一个订单可以包括多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail记录,一对多关系. orderdetail---> orders 一个订单明细只能包括在一个订单中,一对一.   orderd