数据结构之链表

算法通关村第一关 —— 链表青铜挑战笔记

        开门见山,本文简单介绍了数据结构中的链表,并使用java实现一个简单的demo;

        now action

什么是链表?

        链,顾名思义为链子、锁链,其特性为一环扣一环、线性排列;

        好比现在Bob在茫茫人海中找Alice,但他只有一个未知联系人的电话号码(头节点地址),且每个人最多只有一份别人的电话号码(节点地址),现在Bob拨打手头上的电话(头节点),确认一下对方"Are you Alice(节点数据)? No",遂获取该陌生人持有的电话号码call下一位(遍历),如此,到某一位仁兄时,发现"Are you Alice? Yes",即开启了他们的传奇;

        

curd之旅

        既然牵扯到数据,就离不开增删改查,下面就简单的叙述一下链表的curd;

        先上完整版

package Elin.first_level;

public class Link<T> {

    private Node<T> head;
    private Node<T> tail;
    private int length;

    public Link() {
    }

    public int getLength() {
        return this.length;
    }

    public void append(Node<T> addElement) {
        if (addElement == null){
            throw new NullPointerException("节点为空");
        }
        if (this.length == 0){
            this.tail = addElement;
            this.head = addElement;
            this.length++;
            return;
        }
        addElement.setNextNode(null);
        this.tail.setNextNode(addElement);
        this.tail = addElement;
        this.length++;
    }

    //增
    public void add(Node<T> head, Node<T> addElement, int position) {
        if (addElement == null){
            throw new NullPointerException("节点为空");
        }
        //头元素
        if (position == 0) {
            addElement.setNextNode(head);
            head = addElement;
            this.length++;
            return;
        }
        //尾元素
        if (position == this.length - 1) {
            this.tail.setNextNode(addElement);
            this.tail = addElement;
            this.length++;
            return;
        }
        Node<T> node = get(position);
        Node<T> next = node.getNext();
        node.setNextNode(addElement);
        addElement.setNextNode(next);
        this.length++;
    }

    //删
    public void remove(int position) {
        //头元素
        if (position == 0) {
            head = head.getNext();
            this.length--;
            return;
        }
        Node<T> node = get(position - 1);
        Node<T> target = node.getNext();
        //先赋值,再断开
        node.setNextNode(target.getNext());
        target.setNextNode(null);

        //若为最后一个元素,尾节点替换
        if (position == this.length - 1) {
            this.tail = node;
        }

        this.length--;
    }

    //改
    public void set(int position, T val) {
        Node<T> node = get(position);
        node.setVal(val);
    }

    //查
    public Node<T> get(int position) {
        if (this.head == null) {
            throw new NullPointerException("目前为空链表");
        }
        if (position >= length || position < 0) {
            throw new NullPointerException("下标超出范围");
        }
        int len = 0;
        Node<T> node = head;
        while (len != position) {
            len++;
            node = node.getNext();
        }
        return node;
    }

}


class Node<T> {
    /*
     * 值
     * */
    private T val;
    /*
     * 下一个节点
     * */
    private Node<T> nextNode;

    private Node() {
    }

    public Node(T val) {
        this.val = val;
        this.nextNode = null;
    }

    public T getVal() {
        return this.val;
    }

    public void setVal(T val) {
        this.val = val;
    }

    public Node<T> getNext() {
        return this.nextNode;
    }

    public void setNextNode(Node<T> nextNode) {
        this.nextNode = nextNode;
    }

}

       

                从头节点开始遍历,获取相应位置的元素

                

  public Node<T> get(int position) {
        if (this.head == null) {
            throw new NullPointerException("目前为空链表");
        }
        if (position >= length || position < 0) {
            throw new NullPointerException("下标超出范围");
        }
        int len = 0;
        Node<T> node = head;
        while (len != position) {
            len++;
            node = node.getNext();
        }
        return node;
    }
       

        

        在给定位置后追加一个元素,当position = 0时表示为头节点,只需要把新增的节点nextNode设置为当前头节点,然后链表中的头节点换成当前节点就行;当position为链表长度-1时为尾节点,即把尾节点的nextNode设置为新增节点的地址

        此处设置了一个变量length保存当前链表的长度,在增删操作中维护,以便获取长度时的效率更高,当然,添加一个方法遍历获取长度也可以;

        此处有个需要注意的地方,当新增的位置在中间时,需要用临时变量保存当前位置节点(node )的nextNode,或者先把新增节点(addElement)的nextNode赋值为当前位置节点的nextNode,再把当前位置节点(node )的nextNode设置为新增节点(addElement)

//增
public void add(Node<T> head, Node<T> addElement, int position) {
        if (addElement == null){
            throw new NullPointerException("节点为空");
        }
        //头元素
        if (position == 0) {
            addElement.setNextNode(head);
            head = addElement;
            this.length++;
            return;
        }
        //尾元素
        if (position == this.length - 1) {
            this.tail.setNextNode(addElement);
            this.tail = addElement;
            this.length++;
            return;
        }
        Node<T> node = get(position);
        Node<T> next = node.getNext();
        node.setNextNode(addElement);
        addElement.setNextNode(next);
        this.length++;
    }
        

        入参为删除的节点位置(当然根据值的内容删除也可以),当position = 0时,删除头节点,直接把头节点换成下一个节点就行(head = head.getNext()),失去引用的头节点会在young gc时回收掉,当position == this.length - 1时,表示删除尾节点,此时获取倒数第二个节点,把该节点的nextCode设置为null即可;

        若为其他的节点,则老老实实的遍历,把坐标节点的nextCode替换为下下个节点的地址;

        当position == this.length - 1,即为删除尾节点,此处用一变量tail保存了尾节点的值,以便在新增尾节点时效率更高

    //删
    public void remove(int position) {
        //头元素
        if (position == 0) {
            head = head.getNext();
            this.length--;
            return;
        }
        Node<T> node = get(position - 1);
        Node<T> target = node.getNext();
        //先赋值,再断开
        node.setNextNode(target.getNext());
        target.setNextNode(null);

        //若为最后一个元素,尾节点替换
        if (position == this.length - 1) {
            this.tail = node;
        }

        this.length--;
    }
         

          改就简单了,获取坐标节点,更换下值

//改
    public void set(int position, T val) {
        Node<T> node = get(position);
        node.setVal(val);
    }
     
        拓展

        此处仅为一个简单的单向链表的demo,链表还有双向链表、环(循环链表),双向环(双向循环链表)等分支,各具特色,如:

        双向链表

        每个节点不仅记录下一个节点的地址,并且同时记录着上一个节点的地址,此时,单行道变为双行道,数据的遍历方式可以有两种方向

        

        环(循环链表)

        在单项链表的基础上,为节点的下一个节点为头节点,形成一个环

        

        双向环(双向循环链表)

        在循环链表的基础上,加上双向链表的特性,使之成为两个方向互通的环

        位置太窄,后面这几个就不上代码了,绝对不是因为懒得写(手动狗头)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值