数据结构之路 - 单向链表

引言

链表是最常见的数据结构之一,能用来实现栈、队列等很多数据结构;是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针;
优点:长度没有限制,充分利用计算机内存空间,实现灵活的内存动态管理
缺点:随机访问缓慢,同时链表由于增加了结点的指针域,空间开销比较大

单向列表几种操作

添加数据

原图
一个节点中,包含了数据 以及 指向下一个节点的指针
这里写图片描述

头部添加数据

新数据next 指向原来head 节点,并将head 等于 新节点
这里写图片描述

尾部添加数据

遍历到size 前面一个位置,将前面一个位置的next 指向新节点,新节点的next 指向null
这里写图片描述

定点添加数据

遍历到需要添加位置的前一个节点prev,将新节点的next 指向prev的next,再将prev的next 指向新节点
这里写图片描述

删除数据

遍历到指定位置的前一个节点prev,将pre的next 指向 pre next 的next。
可将删除的数据next 指向null,减少资源开支。
这里写图片描述

修改数据

遍历到需要修改的节点,直接修改该节点的数据
这里写图片描述

单向链表的具体实现

head为首节点

/**
 * @author stormxz
 * 单向链表的具体实现
 *
 */
public class LinklistUtil<E> {

        private class Node<E> {
            public E data;                  //节点中保存的数据
            public Node next;            //节点指向下一个节点

            public Node() {              //三个构造方法,用来初始化节点
                this(null, null);
            }

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

            public Node(E data) {
                this.data = data;
            }

            public String toString() {   //打印
                return data + " -> ";
            }

        }

        private int size;  //链表中实际数据量
        private Node head; //链表头部

        //构造方法,初始化size,以及头节点
        public LinklistUtil() {
            size = 0;
            head = null;
        }

        //获得链表中数据总量
        public int getSize() {
            return size;
        }

        //判断当前链表是否为空
        public boolean isEmpty() {
            return size == 0;
        }

        //在头部添加数据
        public void addFirst(E e) {
            //Node node = new Node(e);
            //node.next = head
            //head = node;

            head = new Node(e, head);
            size++;
        }

        //在尾部添加
        public void addLast(E e) {
            addLinkData(size, e);
        }

        //指定索引位置添加数据  从0开始
        public void addLinkData(int index, E e) {
            if (index < 0 && index > size) {
                new IllegalArgumentException("index 有误");
            }
            if (index == 0) {
                addFirst(e);
            } else {
                Node prev = head;
                for (int i = 0; i < index -1; i++) {
                    prev = prev.next;
                }
                //Node newNode = new Node(e);
                //newNode.next = prev.next;
                //prev.next = newNode;

                prev.next = new Node(e, prev.next);
                size++;
            }
        }

        //删除头数据
        public E removeFirst() {
            if (size == 0) {
                throw new IllegalArgumentException("无数据");
            }
            Node nodeFirst = head;
            head = nodeFirst.next;
            nodeFirst.next = null;
            size--;
            return (E) nodeFirst.data;
        }

        //删除末尾数据
        public E removeLast() {
            return removeValue(size - 1);
        }

        //删除索引位置的数据
        public E removeValue(int index) {
            if (index >= size || index < 0) {
                throw new IllegalArgumentException("为空链表 
                || 位置有问题");
            }
            Node nodeIndex = null;
            if (index == 0) {
                return removeFirst();
            } else {
                Node nodeIndexBefore = head;
                for (int i = 0; i < index - 1; i++) {
                    nodeIndexBefore = nodeIndexBefore.next;
                }
                nodeIndex = nodeIndexBefore.next;
                nodeIndexBefore.next = nodeIndex.next;
                nodeIndex.next = null;
            }
            size--;
            return (E) nodeIndex.data;
        }

        //修改数据
        public void updateValue(int index, int value) {
            if (index >= size || index < 0) {
                throw new IllegalArgumentException("为空链表 
                || 位置有问题");
            }
            Node prev = head;
            for (int i = 0; i < index; i++) {
                prev = prev.next;
            }
            prev.data = value;
        }

        //查询索引位置数据
        public E getValue(int index) {
            Node valueNode = head;
            if (index >= size || index < 0) {
                throw new IllegalArgumentException("为空链表
                 || 位置有问题");
            }
            for (int i = 0; i < index; i++) {
                valueNode = valueNode.next;
            }

            return (E) valueNode.data;
        }

        //某位置是否存在某个值
        public boolean isExist(int index, int value) {
            return getValue(index).equals(value);
        }

        //打印
        public String toString() {
            if (size == 0) {
                return "null";
            }
            Node prev = head;
            StringBuilder mStringBuilder = new StringBuilder();
            for (int i = 0; i < size; i++) {
                mStringBuilder.append(prev.toString());
                prev = prev.next;
                if (i == size -1) {
                    mStringBuilder.append("null");
                }
            }
            return mStringBuilder.toString();
        }
}

虚拟头节点

使用虚拟头节点的好处是,简便代码,可将添加首数据的相关代码结合到随机位置处理逻辑中来。由于添加了虚拟节点,那么获得prev的节点,需要遍历index次。
原图
这里写图片描述

/**
 * @author stromxz
 * 
 * 在head 添加一个dumHead 用来指向头   (虚拟头节点)
 *
 */
public class LinkedlistUtil<E> {

    private class Node {
        public E e;
        public Node next;

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

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

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

        public String toString() {
            return e + " -> ";
        }
    }

    private int size; //链表中的成员个数
    private Node head;//链表头
    private Node dumHead;//添加虚拟头

    public LinkedlistUtil() {
        size = 0;
        head = new Node(null, null);
        dumHead = new Node(null, head);  //将链表虚拟头节点指向头节点
    }

    public int getSize() {
        return size;
    }

    //添加首数据
    public void addFirst(E value) {
        addValue(0, value);
    }

    //添加尾数据
    public void addLast(E value) {
        addValue(size, value);
    }

    //在某位置添加数据
    public void addValue(int index, E value) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("添加位置有误");
        }
        Node prev = dumHead;
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }
        //Node node = new Node(value);
        //node.next = prev.next;
        //prev.next = node;

        prev.next = new Node(value, prev.next);
        size++;
    }

    //移除首数据
    public E removeFirst() {
        E e = removeValue(0);
        return e;
    }

    //移除尾数据
    public E removeLast() {
        E e = removeValue(size - 1);
        return e;
    }

    //移除某位置的数据
    public E removeValue(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("删除位置有误");
        }
        Node prev = dumHead;
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }
        Node nodeIndex = prev.next;
        prev.next = nodeIndex.next;
        nodeIndex.next = null;
        size--;

        return nodeIndex.e;
    }

    //更新某位置的数据
    public void updateValue(int index, E value) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("删除位置有误");
        }

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

    //查询某位置的数据
    public E getValue(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("删除位置有误");
        }
        Node cur = dumHead.next;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }
        return cur.e;
    }

    //某位置是否包含某个值
    public boolean isExist(int index, E value) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("删除位置有误");
        }
        Node cur = dumHead.next;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
            if (cur.e.equals(value)) {
                return true;
            }
        }
        return false;
    }

    //是否为空
    public boolean isEmpty() {
        return size == 0;
    }

    //打印
    public String toString() {
        if (size == 0) {
            return "null";
        }
        Node prev = dumHead.next;
        StringBuilder mStringBuilder = new StringBuilder();
        for (int i = 0; i < size; i++) {
            mStringBuilder.append(prev.toString());
            prev = prev.next;
            if (i == size -1) {
                mStringBuilder.append("null");
            }
        }
        return mStringBuilder.toString();
    }

}

测试代码

public class LinkedFuc {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        LinklistUtil<Integer> mLinklistsUtil = new 
                                          LinklistUtil<Integer>();
        System.out.println(mLinklistsUtil.toString());

        //添加数据
        mLinklistsUtil.addLast(1);
        mLinklistsUtil.addLast(2);
        mLinklistsUtil.addLast(3);
        mLinklistsUtil.addLast(4);
        mLinklistsUtil.addLast(5);
        mLinklistsUtil.addLast(6);
        mLinklistsUtil.addLast(7);
        System.out.println(mLinklistsUtil.toString());

        //首部添加数据
        mLinklistsUtil.addFirst(0);
        System.out.println(mLinklistsUtil.toString());

        //指定位置添加
        mLinklistsUtil.addLinkData(8, -2);
        System.out.println(mLinklistsUtil.toString());

        //末尾添加
        mLinklistsUtil.addLast(8);
        System.out.println(mLinklistsUtil.toString());

        //删除首位
        Integer removeValue = mLinklistsUtil.removeFirst();
        System.out.print("removeValue = " + removeValue + " \n");
        System.out.println(mLinklistsUtil.toString());

        //删除1位置
        Integer removeValue1 = mLinklistsUtil.removeValue(8);
        System.out.print("removeValue1 = " + removeValue1 + " \n");
        System.out.println(mLinklistsUtil.toString());

        //删除末尾
        Integer removeValue2 = mLinklistsUtil.removeLast();
        System.out.print("removeValue2 = " + removeValue2 + " \n");
        System.out.println(mLinklistsUtil.toString());

        //更新数据
        mLinklistsUtil.updateValue(6, -3);
        System.out.println(mLinklistsUtil.toString());

        //查询5位置的数据
        Integer mValue = mLinklistsUtil.getValue(5);
        System.out.println("第五位置的value = " + mValue);

        //是否包含某位置的一个值
        boolean isExist= mLinklistsUtil.isExist(4, 1);
        boolean isExist1 = mLinklistsUtil.isExist(4, 5);
        System.out.println("第四位置为1 = " + isExist);
        System.out.print("第四位置为5 = " + isExist1);
    }
}

测试结果

null
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> -2 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> -2 -> 8 -> null
removeValue = 0
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> -2 -> 8 -> null
removeValue1 = 8
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> -2 -> null
removeValue2 = -2
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> null
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> -3 -> null
第五位置的value = 6
第四位置为1 = false
第四位置为5 = true

实现栈功能

根据栈的原理,后进先出。可以理解为,每次都是在链表头部添加,删除数据。
push:入栈
pop:出栈
peak:查看栈顶数据
isEmpty:判空

/**
 * @author stormxz
 *
 * 链表实现栈(addLast addFirst 都行得通)
 */
public class LinkedStack<E> {

    private LinkedlistUtil<E> mLinked = null;

    public LinkedStack() {
        mLinked = new LinkedlistUtil<E>();
    }

    //入栈
    public void push(E value) {
        mLinked.addFirst(value);
    }

    //出栈,并返回该数据
    public E pop() {
        if (mLinked.isEmpty()) {
            return null;
        }
        return mLinked.removeValue(0);
    }

    //查询该数据
    public E peek() {
        if (mLinked.isEmpty()) {
            return null;
        }
        return mLinked.getValue(0);
    }

    //判空
    public boolean isEmpty() {
        return mLinked.isEmpty();
    }

    //打印
    public String toString() {
        return mLinked.toString();
    }

}

测试部分代码:

LinkedStack<Integer> mLinkedStack = new LinkedStack<Integer>();
        mLinkedStack.push(0);
        mLinkedStack.push(1);
        mLinkedStack.push(2);
        mLinkedStack.push(3);
        mLinkedStack.push(4);
        mLinkedStack.push(5);
        System.out.println(mLinkedStack.toString());

        Integer value = mLinkedStack.pop();
        System.out.println("mLinkedStack pop value = " + 
                          value + "     " + mLinkedStack.toString());
        Integer valuee = mLinkedStack.peek();
        System.out.println("mLinkedStack peek value = " + 
                          valuee + "     " + mLinkedStack.toString());

测试结果:
5 -> 4 -> 3 -> 2 -> 1 -> 0 -> null
pop value = 5 4 -> 3 -> 2 -> 1 -> 0 -> null
peek value = 4 4 -> 3 -> 2 -> 1 -> 0 -> null

猜你喜欢

转载自blog.csdn.net/weixin_39158738/article/details/80884228