数据结构之单链表

注:总结自《数据结构》
(1)概念:
单链表属于线性表的一种。
线性表的链式存储结构是用若干地址分散的存储单元存储数据元素,逻辑上相邻的数据元素在物理位置上不一定相邻,必须采用附加信息表示数据元素之间的顺序关系。存储一个数据元素的存储单元至少包含两部分—数据域和地址域。
单链表的特点:
a.有一个位置存放数据元素,有一个指针指向下一个元素,根据某个节点,可以找到数据元素以及它的下一个节点,但是想要找到他的先驱节点就得从头开始遍历,判断某个节点的下一个节点是不是指定的节点。
b.单链表的插入、删除不需要移动位置,只需要修改节点的下一个指向就可以了。时间复杂度是O(1)。
c.单链表查找元素不能像顺序表一样,根据下标查找,只能从头结点开始遍历。时间复杂度是O(n)。
d.转序单链表是从第一个元素开始,将指向下一个节点的指针指向前一个元素,也就是先驱节点。
e.排序单链表的插入就是判断当前插入的节点和已排序的链表中的节点相比,把插入的节点放在第一个比它大的节点的前面。
f.循环双链表是最后一个节点的next指针指向头结点。
g.单链表的深拷贝和浅拷贝和顺序表的性质是一样的。浅拷贝是只对头指针赋值,导致两条单链表的两个头指针指向的是同一个节点。深拷贝是仅复制节点,没有复制对象元素,导致两个节点引用同一个元素对象。深度拷贝是不仅复制所有节点,还复制所有对象元素。

(2)单链表结点:

/**
 * @date 2017-6-04
 * @author liuffei
 * @description 单链表
 */
public class Node<T> {

    public T data; //数据域,保存数据元素
    public Node<T> next;//地址域,引用后继结点

    public Node(T data,Node<T> next){
        this.data = data;
        this.next = next;
    }

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

(3)链表基类

public interface LList<T> {
    boolean isEmpty();//判断线性表是否为空
    void insert(int i,T t);//插入t作为第i个元素
    void set(int i,T t);//设置第i个元素为t
    T get(int i);//获得第i个元素
    int length();//获得线性表的长度
    T remove(int i);//删除第i个元素
    void removeAll();//删除全部元素
    T search(T key);//搜索首次出现关键字为key的元素
    void append(T t);//在线性表尾部追加t元素
}

(4)带头结点的单链表

import gdut.ff.LList;
/**
 * @date 2017-06-04
 * @author liuffei
 * @description 带头结点的单链表
 * @param <T>
 */
public class SinglyLinkedList<T> implements LList<T>{

    public Node<T> head ;//头指针,指向单链表的头结点

    public SinglyLinkedList(){
        head = new Node<T>();
    }

    //深拷贝
    public SinglyLinkedList(SinglyLinkedList<T> list){
        this();
        Node<T> rear = this.head ;
        Node<T> p = list.head.next;
        while(p != null){
            rear.next = new Node<T>(p.data,null);
            rear = rear.next;
            p = p.next;
        }
    }

    //由指定数组中的多个对象构造单链表。采用尾插入构造单链表
    public SinglyLinkedList(T[] element){
        this();
        Node<T> rear = this.head;
        for(int i = 0;i < element.length;i++){
            rear.next = new Node<T>(element[i],null);
            rear = rear.next;
        }
    }

    //判断单链表是否为空
    @Override
    public boolean isEmpty() {
        return this.head.next == null;
    }

    //在位置i插入元素t
    @Override
    public void insert(int i, T t) {
        if(t == null){
            return;
        }
        Node<T> p = this.head;
        for(int j = 0;j < i && p.next != null ;j++){//定位到第i-1个元素
            p = p.next;
        }
        p.next = new Node<T>(t,p.next);
    }

    //修改位置i上元素的值
    @Override
    public void set(int i, T t) {
        if(t == null){
            return ;
        }
        Node<T> current = getNode(i);
        if(null != current){
            current.data = t;
        }
    }

    //拿到位置i的元素,返回结点
    public Node<T> getNode(int i){
        if(i >= 0){
            Node<T> p = this.head.next;
            for(int j = 0;j < i && p != null;j++){
                p = p.next;
            }
            if(p != null){
                return p;
            }
        }else{
            throw new IndexOutOfBoundsException(i+"");
        }
        return null;
    }

    //拿到位置i的元素,返回数值
    @Override
    public T get(int i) {
        Node<T> p = getNode(i);
        if(p != null){
            return p.data;
        }
        return null;
    }

    //线性表的长度
    @Override
    public int length() {
        int len = 0;
        Node<T> p = this.head.next;
        while(null != p){
            len++;
            p = p.next;
        }
        return len;
    }

    //删除指定位置的元素
    @Override
    public T remove(int i) {
        if( i >= 0){
            Node<T> p = this.head;
            for(int j = 0;j < i && p.next != null;j++){
                p = p.next;
            }
            if(p.next != null){
                T old = p.data;
                p.next = p.next.next;
                return old;
            }
        }
        return null;
    }

    //删除全部元素
    @Override
    public void removeAll() {
        this.head.next = null;
    }

    @Override
    public T search(T key) {
        return null;
    }

    //在尾部追加元素
    @Override
    public void append(T t) {
        insert(Integer.MAX_VALUE,t);
    }

    //打印元素
    public String toString(){
        Node<T> p = this.head.next;
        StringBuffer sb = new StringBuffer("(");
        while(p != null){
            sb.append(p.data);
            sb.append(",");
            p = p.next;
        }
        sb.deleteCharAt(sb.length()-1);
        sb.append(")");
        return sb.toString();
    }

    //单链表比较相等
    public boolean equals(Object obj){
        if(this == obj){
            return true;
        }
        if(!(obj instanceof SinglyLinkedList)){
            return false;
        }
        Node<T> p = this.head.next;
        Node<T> q = ((SinglyLinkedList<T>)obj).head.next;
        while(p != null && q != null && p.data.equals(q.data)){
            p = p.next;
            q = q.next;
        }
        return p == null && q == null;
    }
}

(5)求单链表的平均值

/**
 * @date 2017-6-4
 * @author liuffei
 * @description 求线性表的平均值
 */
public class SinglyLinkedList_average {

    private static Random random = new Random();

    /**
     * 创建随机数的单链表
     * @param n 随机数的数量 
     * @return 
     */
    public static SinglyLinkedList randomSerial(int n){
        SinglyLinkedList list = new SinglyLinkedList();
        for(int i = 0;i < n;i++){
            list.append(random.nextInt(100));
        }
        return list;
    }

    /**
     * 求线性表的平均值(减去最大值和最小值,单链表长度小于2的不减去最大值和最小值)
     * @param list
     * @return
     */
    public static double average(SinglyLinkedList<Integer> list){
        if(list.isEmpty()){
            throw new IllegalArgumentException("不能对空单链表计算平均值");
        }
        Node<Integer> p = list.head;
        int min = Integer.MAX_VALUE;//设置最小值为IInteger类型的最大值
        int max = Integer.MIN_VALUE;//设置最大值为Integer类型的最小值
        int sum = 0;//和
        int len = list.length();//长度
        while(p.next != null){
             p = p.next;
             if(p.data < min){
                 min = p.data;
             }
             if(p.data > max){
                 max = p.data;
             }
             sum += p.data;
        }
        if(len > 2){
            sum = sum - min - max;
            len = len - 2;
        }
        System.out.println("max = " + max);
        System.out.println("min = " + min);
        double average = (double)sum / len;//求平均值
        return average;
    }

    public static void main(String args[]){
        SinglyLinkedList list = randomSerial(10);
        System.out.println(list.toString());
        System.out.println("average = " + average(list));

        SinglyLinkedList list2 = randomSerial(1);
        System.out.println(list2.toString());
        System.out.println("average = " + average(list2));
    }
}

(6)求单链表的逆转

/**
 * @date 2017-06-04
 * @author liuffei
 * @description 单链表转序
 */
public class SinglyLinkedList_reverse {

    public static SinglyLinkedList<String> reverse(SinglyLinkedList<String> list){
        Node<String> p = list.head.next;
        Node<String> next = null;
        Node<String> front = null;
        while(p != null){
            next = p.next;//记住当前节点的下一个节点
            p.next = front;//当前节点的下一个节点变成前趋节点
            front = p;//前趋节点和当前节点都向后移动一位
            p = next;
        }
        //头指针的后继结点指向最后一个节点
        list.head.next = front;
        return list;
    }

    public static void main(String args[]){
        String values[] = {"A","B","C","D","E"};
        SinglyLinkedList<String> list = new SinglyLinkedList(values);
        System.out.println(list.toString());
        list = reverse(list);
        System.out.println(list.toString());
    }
}

(7)排序单链表

/**
 * @date 2017-06-04
 * @author liuffei
 * @description 排序单链表
 * @param <T>
 */
public class SortedSinglyLinkedList<T extends Comparable<T>> extends SinglyLinkedList<T> {

    //默认构造方法,调用父类默认构造方法
    public SortedSinglyLinkedList(){
        super();
    }

    public SortedSinglyLinkedList(T[] element){
        super();
        if(element != null){
            for(int i = 0;i < element.length;i++){
                insert(element[i]);
            }
        }
    }

    public SortedSinglyLinkedList(SortedSinglyLinkedList<T> list){
        super(list);
    }

    public void insert(T t){
        if(t == null){
            return;
        }
        Node<T> front = this.head;
        Node<T> p = front.next;
        while(p != null && p.data.compareTo(t) < 0){
            front = p;
            p = p.next;
        }
        front.next = new Node<T>(t,p);
    }

    public void remove(T t){
        if(t == null){
            return;
        }
        Node<T> front = this.head;
        Node<T> p = front.next;
        while(p != null && p.data.compareTo(t) < 0){
            front = p;
            p = p.next;
        }
        if(p != null && p.data.compareTo(t) == 0){
            front.next = p.next;
        }
    }

    public void insert(int i,T t){
        throw new UnsupportedOperationException("insert(int i,T x)");
    }

    public void append(T t){
        throw new UnsupportedOperationException("append(T t)");
    }
}

(8)单链表解决约瑟夫环问题

public class Josephus_SinglyLinkedList {

    public static void remove(SinglyLinkedList list,int distance,int live){
        int begin  = 0;
        while(list.length() > live){
            begin = (begin+distance-1)%list.length(); 
            list.remove(begin);
        }
    }

    public static void main(String args[]){
        SinglyLinkedList list = new SinglyLinkedList();
        list.append("A");
        list.append("B");
        list.append("C");
        list.append("D");
        list.append("E");
        System.out.println(list.get(3));
        System.out.println(list.length());
        System.out.println(list.toString());
        remove(list,2,1);
        System.out.println(list.toString());
    }
}

(9)循环单链表

/**
 * @date 2017-6-5
 * @author liuffei
 * @description 循环单链表
 * @param <T>
 */
public class CirSinglyLinkedList<T> {

    public Node<T> head;

    public CirSinglyLinkedList(){
        this.head = new Node<T>();
        this.head.next = this.head;
    }

    public boolean isEmpty(){
        return this.head.next == this.head;
    }

    public String toString(){
        StringBuffer sb = new StringBuffer("(");
        Node<T> p = this.head.next;
        while(p != null){
            sb.append(p.data);
            sb.append(",");
            p = p.next;
        }
        sb.deleteCharAt(sb.length()-1);
        sb.append(")");
        return sb.toString();
    }
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第0章 Java程序设计基础 1 【习0.1】 实验0.1 哥德巴赫猜想。 1 【习0.2】 实验0.2 杨辉三角形。 1 【习0.3】 实验0.3 金额的中文大写形式。 1 【习0.4】 实验0.4 下标和相等的数字方阵。 1 【习0.5】 实验0.5 找出一个二维数组的鞍点 2 【习0.6】 实验0.6 复数类。 2 【习0.7】 实验0.8 图形接口与实现图形接口的类 2 第1章 绪论 3 【习1.1】 实验1.1 判断数组元素是否已按升序排序。 3 【习1.2】 实验1.3 用递归算法求两个整数的最大公因数。 3 第2章 线性表 5 【习2.1】 习2-5 图2.19的数据结构声明。 5 【习2.2】 习2-6 如果在遍历单链表时,将p=p.next语句写成p.next=p,结果会怎样? 5 【习2.3】 实验2.2 由指定数组中的多个对象构造单链表。 5 【习2.4】 实验2.2 单链表的查找、包含、删除操作详见8.2.1。 5 【习2.5】 实验2.2 单链表的替换操作。 6 【习2.6】 实验2.2 首尾相接地连接两条单链表。 6 【习2.7】 实验2.2 复制单链表。 6 【习2.8】 实验2.2 单链表构造、复制、比较等操作的递归方法。 7 【习2.9】 建立按升序排序的单链表(不带头结点)。 8 【习2.10】 实验2.6 带头结点的循环双链表类,实现线性表接口。 10 【习2.11】 实验2.5 建立按升序排序的循环双链表。 14 第3章 栈和队列 17 【习3.1】 习3-5 栈和队列有何异同? 17 【习3.2】 能否将栈声明为继承线性表,入栈方法是add(0,e),出栈方法是remove(0)?为什么? 17 【习3.3】 能否用一个线性表作为栈的成员变量,入栈方法是add(0,e),出栈方法是remove(0)?为什么? 17 【习3.4】 能否将队列声明为继承线性表,入队方法是add(e),出队方法是remove(0)?为什么? 17 第4章 串 18 【习4.1】 实验4.6 找出两个字符串中所有共同的字符。 18 【习4.2】 习4-9(1) 已知目标串为"abbaba"、模式串为"aba",画出其KMP算法的匹配过程,并给出比较次数。 18 【习4.3】 习4-9(2) 已知target="ababaab"、pattern="aab",求模式串的next数组,画出其KMP算法的匹配过程,并给出比较次数。 18 第5章 数组和广义表 20 【习5.1】 求一个矩阵的转置矩阵。 20 第6章 树和二叉树 21 【习6.1】 画出3个结点的各种形态的树和二叉树。 21 【习6.2】 找出分别满足下面条件的所有二叉树。 21 【习6.3】 输出叶子结点。 21 【习6.4】 求一棵二叉树的叶子结点个数。 22 【习6.5】 判断两棵二叉树是否相等。 22 【习6.6】 复制一棵二叉树。 23 【习6.7】 二叉树的替换操作。 23 【习6.8】 后根次序遍历中序线索二叉树。 24 第7章 图 25 第8章 查找 26 【习8.1】 实验8.1 顺序表的查找、删除、替换、比较操作。 26 【习8.2】 实验8.2 单链表的全部替换操作。 28 【习8.3】 实验8.2 单链表的全部删除操作。 28 【习8.4】 折半查找的递归算法。 29 【习8.5】 二叉排序树查找的递归算法。 29 【习8.6】 二叉排序树插入结点的非递归算法。 30 【习8.7】 判断一棵二叉树是否为二叉排序树。 31 第9章 排序 32 【习9.1】 判断一个数据序列是否为最小堆序列。 32 【习9.2】 归并两条排序的单链表32 【习9.3】 说明二叉排序树与堆的差别。 34
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值