单链表的基本操作

本文介绍了一个简单的单链表类的实现,包括插入、删除、查找等基本操作,并提供了链表的显示方法及反向输出功能。
package ccnu.linkList;

public class LinkList {
    private Node first; // 第一个节点
    private Node rear; // 最后一个节点
    private int size; // 当前链表的大小

    public LinkList() {
        this.first = null;
        this.rear = null;
        this.size = 0;
    }

    /**
     * 链表中的元素的个数
     * 
     * @return 返回该链表中元素的个数
     */
    public int size() {
        return size;
    }

    /**
     * 判断当前对象是否为空,如果空,当且仅当没有任何元素,{@link #size()}为0
     * 
     * @return true,如果{@link #size()}为0,否则false
     */
    public boolean isEmpty() {
        if (size == 0) {
            return true;
        }
        return false;
    }

    /**
     * 头插法,在链表的头部插入节点
     * 
     * @param data
     *            插入节点的数据
     * @see #insertAtRear(int)
     * @see #insertByIndex(int, int)
     */
    public void insertAtHead(int data) {
        Node node = new Node(data);
        size++;
        if (size == 1) { // 第一个节点采用头插法
            first = node;
            rear = node;
        } else {
            node.next = first; // 当前节点插入到最前
            first = node;
        }
    }

    /**
     * 尾插法,在链表的尾部插入节点
     * 
     * @param data
     *            插入节点的数据
     * @see #insertAtHead(int)
     * @see #insertByIndex(int, int)
     */
    public void insertAtRear(int data) {
        Node node = new Node(data);
        size++;
        if (size == 1) { // 第一个节点采用尾插法
            first = node;
            rear = node;
        } else {
            rear.next = node; // 当前节点插入到最后
            rear = node;
        }
    }

    /**
     * 指定索引位置插入节点(其后面)
     * 
     * @param index
     *            插入位置
     * @param data
     *            所要插入节点的数据
     * @see #insertAtHead(int)
     * @see #insertAtRear(int)
     */
    public void insertByIndex(int index, int data) {
        if (index < 0 || index > (size - 1)) {
            return;
        }
        int pos = 0;
        Node current = first;
        Node node = new Node(data);
        while (pos != index) {
            pos++;
            current = current.next;
        }
        node.next = current.next;
        current.next = node;
        if(node.next == null){ // 插入的位置为最后一个位置
            rear = node;
        }
        size++;
    }

    /**
     * 删除任意索引位置的节点,并返回该节点,返回null,如果位置不合法
     * 
     * @param index
     *            指定位置
     * @return 返回将要删除的节点
     * @see #deleteByData(int, boolean)
     * @see #deleteByData(int)
     */
    public Node deleteByIndex(int index) {
        Node current = first; // 当前节点
        Node previous = null; // 当前节点的前一个节点
        int pos = 0;
        if (index < 0 || index >= size) { // 超出索引范围
            return null;
        } else {
            while (pos != index) {
                pos++;
                previous = current;
                current = current.next;
            }
        }
        if (current == first) { // 删除节点为第一个节点
            first = first.next;
        } else {
            previous.next = current.next;
        }
        if (current.next == null) { // 删除节点为最后一个节点
            rear = previous;
        }
        size--;
        // current.next = null; // 仅保留数据域
        return current; // 返回指定索引的节点
    }

    /**
     * 删除链表中指定数据的节点,并返回删除的节点(集)
     * 
     * @param data
     *            数据域
     * @param flag
     *            true表示删除所有data元素,缺省为false
     * @return 表示"删除链表"的第一个节点
     * @see #deleteByData(int)
     */
    public Node deleteByData(int data, boolean flag) { // flag = true
                                                        // 表示删除所有值为data的节点,否则只删除出现的第一个值为data节点
        Node newFirst = null; // 所有删除指定data的链表的第一个节点
        Node newRear = null;
        int pos = 0;
        Node current = first;
        if (flag == false) {
            while (current != null) {
                if (current.data == data) {
                    deleteByIndex(pos);
                    // return (newFirst = current);
                    newFirst = current;
                    newFirst.next = null; // 只保留数据域
                    break;
                }
                current = current.next;
                pos++; // 记录当前索引位置
            }
        } else {
            while (current != null) {
                if (current.data == data) {
                    deleteByIndex(pos);
                    pos--; // 删除链表中一个节点后,当前最新链表元素少一个,对应的索引也要减少
                    if (newFirst == null) { // 在"删除链表"中插入第一个节点
                        newFirst = current;
                        newRear = current;
                    } else {
                        newRear.next = current;
                        newRear = current;
                    }
                }
                current = current.next;
                pos++;
            }
        }
        return newFirst;
    }

    /**
     * 仅删除链表中的第一个为指定数据的节点,并返回这个节点
     * 
     * @param data
     *            指定数据
     * @return 删除的节点
     * @see #deleteByData(int, boolean)
     */
    public Node deleteByData(int data) {
        return deleteByData(data, false);
    }

    /**
     * 根据指定数据在链表中获取其位置 (首次出现)
     * 
     * @param data
     *            数据
     * @return 数据的位置,返回-1,如果链表中木有这个数据
     */
    public int findByData(int data) {
        Node current = first;
        int pos = 0;
        while (current != null) {
            if (current.data == data) {
                return pos;
            }
            current = current.next;
            pos++;
        }
        return -1;
    }

    /**
     * 根据指定的位置获取这个节点
     * 
     * @param index
     *            索引位置
     * @return 指定位置的节点
     */
    public Node findByIndex(int index) {
        Node current = first;
        int pos = 0;
        while (current != null) {
            if (pos == index) {
                return current;
            }
            current = current.next;
            pos++;
        }
        return null;
    }

    /**
     * 用新数据替代指定数据 (修改一个,如果存在)
     * 
     * @param oldData
     *            待查数据
     * @param newData
     *            新数据
     * @see #replaceAll(int, int)
     */
    public void replace(int oldData, int newData) {
        Node current = first;
        while (current != null) {
            if (current.data == oldData) {
                current.data = newData;
                return;
            }
            current = current.next;
        }
    }

    /**
     * 用新数据替代指定数据 (修改所有,如果存在)
     * 
     * @param oldData
     *            待查数据
     * @param newData
     *            新数据
     * @see #replace(int, int)
     */
    public void replaceAll(int oldData, int newData) {
        Node current = first;
        while (current != null) {
            if (current.data == oldData) {
                current.data = newData;
            }
            current = current.next;
        }
    }

    /**
     * 显示整个链表的信息
     *
     */
    public void displayAllNodes() {
        Node current = first;
        while (current != null) {
            current.display();
            current = current.next;
        }
    }
/**
     * 
     * 反向输出单链表所有值(递归)
     */
    public void reverseOutput(Node first){
        if(first == null){
            return;
        }
        Node cur = first;
        if(cur.next == null){
            System.out.print(new Integer(cur.data).toString() + " ");
        }else{
            reverseOutput(cur.next);
            System.out.print(new Integer(cur.data).toString() + " ");

        }
    }
    /**
     * 反向输出单链表所有值(栈存储方式)
     * 
     */
    public void reverseOutput2(Node first){
        List<Node> l = new LinkedList<Node>();
        Node node = first;
        while(node != null){
            l.add(node);
            node = node.next;
        }
        while(l.size() != 0){
            System.out.print(new Integer(l.remove(l.size()-1).data).toString() + " ");
        }
    }

    /**
     * 将原单链表进行倒置
     * @param first 原单链表的头结点
     * @return 倒置后单链表的头结点
     */
    public Node reverseList(Node first){
        if(first == null){
            return null;
        }
        Node p, q;
        p = first.next;
        first.next = null;
        while(p != null){
            q = p;
            p = p.next;
            q.next = first;
            first = q;
        }
        return first;
    }

}


package ccnu.linkList;

public class Node {
    // 私有,设置getter和setter
    protected Node next; // 指针域
    protected int data; // 数据域

    public Node() {
        this(0);
    }

    public Node(int data) {
        this.data = data;
        this.next = null;
    }

    public void display() {
        if (this.next != null) {
            System.out.print(data + "-->");
        } else {
            System.out.print(data);
        }
    }

    @Override
    public String toString() {
        return "Node [data=" + data + "]";
    }


}

1、链接存储方法<br>  链接方式存储的线性表简称为链表(Linked List)。<br>  链表的具体存储表示为:<br>  ① 用一组任意的存储单元来存放线性表的结点(这组存储单元既可以是连续的,也可以是不连续的)<br>  ② 链表中结点的逻辑次序和物理次序不一定相同。为了能正确表示结点间的逻辑关系,在存储每个结点值的同时,还必须存储指示其后继结点的地址(或位置)信息(称为指针(pointer)或链(link))<br>注意:<br>  链式存储是最常用的存储方式之一,它不仅可用来表示线性表,而且可用来表示各种非线性的数据结构。<br><br>2、链表的结点结构<br> ┌──┬──┐<br> |data | next│<br> └──┴──┘ <br>  data域--存放结点值的数据域<br>  next域--存放结点的直接后继的地址(位置)的指针域(链域)<br>注意:<br>   ①链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起的。<br>  ②每个结点只有一个链域的链表称为单链表(Single Linked List)。<br>3、头指针head和终端结点指针域的表示<br>  单链表中每个结点的存储地址是存放在其前趋结点next域中,而开始结点无前趋,故应设头指针head指向开始结点。<br>注意:<br>  链表由头指针唯一确定,单链表可以用头指针的名字来命名。<br>【例】头指针名是head的链表可称为表head。<br>  终端结点无后继,故终端结点的指针域为空,即NULL<br>4、单链表类型描述<br> typedef char DataType; /* 假设结点的数据域类型为字符 */<br> typedef struct node { /* 结点类型定义 */<br> DataType data; /* 结点的数据域 */<br> struct node *next; /* 结点的指针域 */<br> } ListNode;<br> typedef ListNode *LinkList;<br> ListNode *p;<br> LinkList head;<br> 注意:<br>  ①LinkList和ListNode *是不同名字的同一个指针类型(命名的不同是为了概念上更明确)<br>  ②LinkList类型的指针变量head表示它是单链表的头指针<br>  ③ListNode *类型的指针变量p表示它是指向某一结点的指针<br><br>6、指针变量和结点变量<br><br><br>┌────┬────────────┬─────────────┐ <br>│    │    指针变量    │     结点变量    │<br>├────┼────────────┼─────────────┤<br>│ 定义 │在变量说明部分显式定义 │在程序执行时,通过标准 │<br>│ │ │函数malloc生成 │<br>├────┼────────────┼─────────────┤<br>│ 取值 │ 非空时,存放某类型结点 │实际存放结点各域内容 │<br>│ │ 的地址 | │<br>├────┼────────────┼─────────────┤<br>│操作方式│ 通过指针变量名访问 │ 通过指针生成、访问和释放 │<br>└────┴────────────┴─────────────┘<br> <br>①生成结点变量的标准函数<br>  p = malloc( sizeof(ListNode) );<br> /* 函数malloc分配一个类型为ListNode的结点变量的空间,并将其首地址放入指针变量p中 */<br>②释放结点变量空间的标准函数 <br>  free(p); /* 释放p所指的结点变量空间 */<br>③结点分量的访问 <br>  利用结点变量的名字*p访问结点分量<br> 方法一:(*p).data和(*p).next<br> 方法二:p-﹥data和p-﹥next<br>④指针变量p和结点变量*p的关系 <br>  指针变量p的值——结点地址<br>  结点变量*p的值——结点内容<br>  (*p).data的值——p指针所指结点的data域的值<br>  (*p).next的值——*p后继结点的地址<br>  *((*p).next)——*p后继结点<br><br>注意:<br>   ① 若指针变量p的值为空(NULL),则它不指向任何结点。此时,若通过*p来访问结点就意味着访问一个不存在的变量,从而引起程序的错误。<br>   ② 有关指针类型的意义和说明方式的详细解释,【参考C语言的有关资料】。<br>
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值