单向链表

没个节点只保存了下一个节点的地址,只能从头节点开始向后遍历。

java 单项链表 java单链表如何遍历_结点

 

java 单项链表 java单链表如何遍历_java_02

 创建一个单链表

/**
 * 基于int的单链表 - 火车
 * 真正被用户使用的是火车类-单链表对象
 **/
public class SingleLinedList {
    // 链表的头节点
    private Node head = null;
    // 当前链表中Node节点的个数 = 有效数值的个数
    private int size = 0;


    /**
     * 单链表的具体的每个节点 - 车厢类
     */
    class Node {
        int val; // 每个节点保存的值
        Node next; // 当前节点的下一个节点地址。
    }

如何遍历一个单链表?

从当前头节点开始,依次取出每个节点值,然后通过next引用走到下一个节点,直到走到链表的末尾(next = null)。

注意:遍历过程中不能直接使用head引用!!!直接使用head引用的话,遍历一次链表后,链表就丢了,此时需要使用过一个临时变量,暂存head的值。

public String toString() {
        String ret = "";
        Node x = head;
        while (x != null) {
            ret += x.val;
            ret += "->";
            x = x.next;
        }
        ret += "NULL";
        return ret;
    }

添加

头插法:

添加元素在链表

java 单项链表 java单链表如何遍历_java 单项链表_03

注意: 必须是先1后2 ,如果顺序反了会丢失原来的链表头节点。!!!

/**
     * 向当前的链表中添加一个新的节点,默认在链表的头部添加
     * @param val
     */
    public void addFirst(int val) {
        Node newNode = new Node();
        // 保存值
        newNode.val = val;
        if(head != null) {
            newNode.next = head;
        }
        // 无论是否为空,新节点都是插入后的链表头结点
        head = newNode;
        size ++;
    }

在链表index位置插入:

 单链表最核心的操作是:找前驱!!!

 要把10新节点插入到索引为2的位置,首先要找到待插入位置的“前驱”。

java 单项链表 java单链表如何遍历_数据结构_04

同样的需要注意代码顺序!!!

public void add(int index,int val) {
        // 1.若index位置非法的
        if (index < 0 || index > size) {
            System.err.println("add index illegal!");
            return;
        }
        // 2.插入任意位置要找到前驱结点,但是链表中有一个结点没有前驱
        // 头结点没有前驱!!!
        if (index == 0) {
            // 头插!
            addFirst(val);
        }else {
            // 3.当前索引合法且不是在数组的头部插入,就要找到插入位置的前驱结点
            Node newNode = new Node();
            newNode.val = val;
            Node prev = head;
            for (int i = 0; i < index - 1; i++) {
                prev = prev.next;
            }
            // 此时prev一定指向了待插入节点的前驱
            newNode.next = prev.next;
            prev.next = newNode;
            size ++;
        }
    }

尾插法:

/**
     * 在链表的尾部插入新元素
     * @param val
     */
    public void addLast(int val) {
        
        add(size,val);
    }

查询

boolean contains(int val):

是否包含指定值的结点 

/**
     * 查找第一个值为val的结点索引是多少
     * @param val
     * @return
     */
    public int getByValue(int val) {
        int index = 0;
        for (Node x = head;x != null;x = x.next) {
            if (x.val == val) {
                // 第一个值为val的节点
                return index;
            }
            index ++;
        }
        // 说明当前链表中根本就没有值为val的结点,返回-1,表示不存在
        return -1;
    }

get(int index):

查询索引为index的元素值是多少。

public boolean contains(int val) {
        int index = getByValue(val);
        return index != -1;
        if (index == -1) {
           return false;       
         }
        return true;
}

igetByValue(int val):

查询第一个值为val的结点索引是多少

因为在修改和删除操作时也需要对index索引位置的合法性进行判断,所以单独创建一个方法判断index的合法性。

private boolean rangeCheck(int index) {
        // size是当前有效元素的下一个索引
        if (index < 0 || index >= size) {
            return false;
        }
        return true;
    }
/**
     * 查询索引为index位置的结点值
     * @param index
     * @return
     */
    public int get(int index) {
        // 1.判断index的合法性
        if (rangeCheck(index)) {
            Node x = head;
            for (int i = 0; i < index; i++) {
                x = x.next;
            }
            return x.val;
        }
        System.err.println("index illegal!get error");
        return -1;
    }

修改

set(int index,int newVal):

修改索引为index位置的结点值为newVal
 

/**
     * 修改索引为index位置的结点值为newVal,返回修改前的节点值
     * @param index
     * @param newVal
     * @return
     */
    public int set(int index,int newVal) {
        if (rangeCheck(index)) {
            Node x = head;
            for (int i = 0; i < index; i++) {
                x = x.next;
            }
            int oldVal = x.val;
            x.val = newVal;
            return oldVal;
        }
        System.err.println("index illegal!set error");
        return -1;
    }

删除

(找前驱)

remove(int index):

删除索引为index位置的元素

/**
     * 删除单链表中索引为index位置的节点,返回删除前的节点值
     * @param index
     * @return
     */
    public int remove(int index) {
        if (rangeCheck(index)) {
            if (index == 0) {
                // 删除头结点
                Node x = head;
                head = head.next;
                size --;
                x.next = null;
                return x.val;
            }else {
                // 此时删除的是链表的中间结点
                // 找前驱!
                Node prev = head;
                for (int i = 0; i < index - 1; i++) {
                    prev = prev.next;
                }
                // prev就是待删除节点的前驱
                // node节点就是待删除的结点
                Node node = prev.next;
                prev.next = node.next;
                node.next = null;
                size --;
                return node.val;
            }

        }
        System.err.println("remove index illegal!");
        return -1;
    }

removeValueOnce(int val):

删除第一个值为val的元素

思路:

①先判断头节点的值是不是等于val

②head.val != val 时,我们现在要找到待删除的节点的前驱,保证程序中前驱节点一定不是待删除的节点。此时prev一定不是待删除结点,prev.val !=val,因此我们判断节点值是否等于待删除的结点,至少也是从当前结点的下一个开始的!

/**
     * 删除链表中第一个值为val的元素
     * @param val
     */
    public void removeValueOnce(int val) {
        if (head == null) {
            System.err.println("链表为空,无法删除");
            return;
        }
        if (head.val == val) {
            // 此时头结点就是第一个值为val的结点
            Node x = head;
            head = head.next;
            x.next = null;
            size --;
        }else {
            // 当前头结点不是待删除的结点,需要遍历
            // 这里同样找到待删除的前驱结点
            // 能否知道前驱结点移动步数?
            Node prev = head;
            while (prev.next != null) {
                // 至少还有后继结点
                if (prev.next.val == val) {
                    // 此时prev.next就是待删除的结点
                    Node node = prev.next;
                    prev.next = node.next;
                    node.next = null;
                    size --;
                    return;
                }
                prev = prev.next;
            }
        }
    }

removeAllValue(int val):

删除链表中所有值为val的元素

思路和上面删除第一个值为val的元素只有一点点不同,问题的关键在于删除一个节点后,prev不能简单的向后移动,一定要确保删除后prev.next.val != val才能移动prev移动。

java 单项链表 java单链表如何遍历_java_05

 

/**
     * 删除链表中所有值为val的结点
     * @param val
     */
    public void removeAllValue(int val) {
        while (head != null && head.val == val) {
            // 头节点就是待删除节点
            Node x = head;
            head = head.next;
            x.next = null;
            size --;
        }
        // 头节点一定不是待删除的节点
        // 判空
        if (head == null) {
            // 链表删完了
            return;
        }else {
            Node prev = head;
            while (prev.next != null) {
                // 至少还有后继结点
                if (prev.next.val == val) {
                    // 此时prev.next就是待删除的结点
                    Node node = prev.next;
                    prev.next = node.next;
                    node.next = null;
                    size --;
                }else {
                    // 只有当prev.next.val != val才能移动prev指针!
                    prev = prev.next;
                }
            }
        }
    }