数据结构和算法2-线性-链

数据结构和算法系列共八篇

1.线性表

线性表是n个类型相同数据元素的有限序列。(a0 ,a1…, ax)

线性表特点

  • 元素类型是相同的

一个线性表里元素要相同。元素可以int、str、对象等,但同一类型要在一个线性表种。

  • 有序

有头有尾,其他元素前后是1:1对应的

  • 有限

线性表是有length的。0代表空的

2.逻辑结构

img

这只是逻辑上的概念,有头有尾,中间一个个紧挨着1:1,是有限的。

比如排队 (只要它们满足线性表的特征即可)

  • 一个个前后站位,站的整整齐齐
  • 一个个拉着绳子,站的歪歪扭扭

3.存储结构

存储结构分

  • 顺序表----顺序存储结构
  • 链表----链式存储结构

3-1.顺序表

img

特点:在内存中分配连续的空间只存储数据,不需要存储地址信息。位置就隐含着地址。

优点

  • 节省存储空间,只存数据,不存地址
  • 索引查找效率高,即每一个结点对应一个序号,由该序号可以直接计算出来结点的存储地址。

元素类型定了,位置知道了,那么存储的在内存的位置就很容器确定了(头地址+位置*元素类型大小)

缺点

  • 插入和删除(中间的位置元素操作)效率低,因为是连续空间,要移动一些操作
  • 按着下标/索引,查询速度快.如果按着值内容查,那么就得全部遍历了
  • 空间是必须连续的,得提前分配好空间.(定义了多了,但存的少了,可能导致空闲浪费)

3-2.链表

img

特点:元素的存储对应的是不连续的存储空间,每个存储结点除了数据外,还要记录自己所在位置(数据域和指针域)。

优点

  • 插入、删除灵活 (不必移动节点,只要改变节点中的指针,但是需要先定位到元素上)
  • 有元素才会分配结点空间,不会有闲置的结点。

缺点

  • 比顺序存储结构的存储密度小 (要存指针域,所以站的空间多了点)。
  • 查询慢,要根据已知节点(链头)慢慢找

4.接口

list常见操作方法

public interface IList {
	// 返回线性表的大小,即数据元素的个数。
	public int size();
	
	// 返回线性表中序号为 i 的数据元素
	public Object get(int i);

	// 如果线性表为空返回 true,否则返回 false。
	public boolean isEmpty();

	// 判断线性表是否包含数据元素 e
	public boolean contains(Object e);

	// 返回数据元素 e 在线性表中的序号
	public int indexOf(Object e);

	// 将数据元素 e 插入到线性表中 i 号位置
	public void add(int i, Object e);

	// 将数据元素 e 插入到线性表末尾
	public void add(Object e);

	// 将数据元素 e 插入到元素 obj 之前
	public boolean addBefore(Object obj, Object e);

	// 将数据元素 e 插入到元素 obj 之后
	public boolean addAfter(Object obj, Object e);

	// 删除线性表中序号为 i 的元素,并返回之
	public Object remove(int i);

	// 删除线性表中第一个与 e 相同的元素
	public boolean remove(Object e);

	// 替换线性表中序号为 i 的数据元素为 e,返回原数据元素
	public Object replace(int i, Object e);
}

5.顺序表实现

实现类


import java.util.Arrays;

/**
 * 顺序表
 *
 * @author: iworkh-沐雨云楼
 * @date: 2020-07-01
 */
public class MyArrayList implements IList {

    // 底层数组来存储多个元素
    private Object[] elementData;

    // 存储的元素的个数,线性表的长度,(不是数组的长度)
    // 因为数组长度是会动态扩容,会比数据多,不然每次操作都要扩数组太低效了
    private int size;

    private static final int DEFAULT_INIT_SIZE = 16;

    public MyArrayList(int initSize) {
        if(initSize < 0){
            throw new RuntimeException("初始长度要大于0:"+initSize);
        }

        this.elementData = new Object[initSize];
    }

    public MyArrayList() {
        this(DEFAULT_INIT_SIZE);
    }

    @Override
    public int size() {
        return this.size;
    }

    @Override
    public Object get(int i) {
        if (isIndexOutOfBounds(i)) {
            throw new IndexOutOfBoundsException("数组越界异常:" + i);
        }
        return elementData[i];
    }

    @Override
    public boolean isEmpty() {
        return this.size == 0;
    }

    @Override
    public boolean contains(Object e) {
        // 通过indexOf方法返回值来处理
        int findIndex = this.indexOf(e);
        return findIndex > 0;
    }

    @Override
    public int indexOf(Object e) {
        if (e == null) {
            for (int i = 0; i < size; i++) {
                if (elementData[i] == null) {
                    return i;
                }
            }
        } else {
            for (int i = 0; i < size; i++) {
                if (e.equals(elementData[i])) {
                    return i;
                }
            }
        }

        return -1;
    }

    @Override
    public void add(int i, Object e) {
        // i有没超过size值
        if (isIndexOutOfBounds(i)) {
            throw new IndexOutOfBoundsException("数组越界异常:" + i);
        }

        // 需要扩容?
        if (this.size == this.elementData.length) {
            grow();
        }

        // i后面数据要移位
        // for (int num = this.size; num > i; num--) {
        //     this.elementData[num] = this.elementData[num - 1];
        // }
        if (this.size > i) {
            System.arraycopy(this.elementData, i, this.elementData, i + 1, this.size - i);
        }

        // 插入i位置值
        this.elementData[i] = e;
        this.size++;
    }

    private void grow() {
        // 扩容一倍
        this.elementData = Arrays.copyOf(elementData, this.elementData.length * 2);
    }

    @Override
    public void add(Object e) {
        // 需要扩容?
        if (this.size == this.elementData.length) {
            grow();
        }
        this.elementData[size] = e;
        this.size++;
    }

    @Override
    public boolean addBefore(Object obj, Object e) {

        if (this.size==this.elementData.length){
            grow();
        }

        // 先找到obj的位置
        int foundIndex = this.indexOf(obj);

        // 1,2,3,4,5,6
        // 移动obj以及后面的元素
        if (this.size > foundIndex) {
            System.arraycopy(this.elementData, foundIndex,
                             this.elementData, foundIndex + 1,
                             this.size - foundIndex);
        }

        // 然后插入e在obj原来位置
        this.elementData[foundIndex] = e;
        // 个数加1
        this.size++;
        return true;
    }

    @Override
    public boolean addAfter(Object obj, Object e) {

        if (this.size==this.elementData.length){
            grow();
        }

        // 先找到obj的位置
        int foundIndex = this.indexOf(obj);

        // 1,2,3,4,5,6
        // 移动obj后面的元素
        if (this.size > foundIndex) {
            System.arraycopy(this.elementData, foundIndex + 1,
                             this.elementData, foundIndex + 2,
                             this.size - foundIndex - 1);
        }

        // 然后插入e在obj后面
        this.elementData[foundIndex + 1] = e;
        // 个数加1
        this.size++;
        return true;
    }

    @Override
    public Object remove(int i) {
        if (isIndexOutOfBounds(i)) {
            throw new IndexOutOfBoundsException("数组越界异常:" + i);
        }
        Object oldValue = this.elementData[i];
        fastRemove(i);
        return oldValue;
    }

    @Override
    public boolean remove(Object e) {
        if (e == null) {
            for (int index = 0; index < size; index++) {
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
            }
        } else {
            for (int index = 0; index < size; index++) {
                if (e.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
            }
        }
        return false;
    }

    private void fastRemove(int index) {
        int numMoved = size - index - 1;
        if (numMoved > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        }
        // clear to let GC do its work
        elementData[--size] = null;
    }

    @Override
    public Object replace(int i, Object e) {
        if (isIndexOutOfBounds(i)) {
            throw new IndexOutOfBoundsException("数组越界异常:" + i);
        }

        this.elementData[i] = e;
        return e;
    }

    private boolean isIndexOutOfBounds(int index) {
        return index < 0 || index > this.size;
    }

    @Override
    public String toString() {
        // 为了简单,这直接使用Arrays.toString来打印了。(这有null的也打印了)
        // 其实应该for循环,取出size以内的元素。
        return "MyArrayList{" + "elementData=" + Arrays.toString(elementData) + '}';
    }
}

测试类

public class MyArrayListTest {

    public static void main(String[] args) {
        IList list = new MyArrayList();
        System.out.println(list.isEmpty());
        list.add("a");
        System.out.println(list.isEmpty());
        list.add("c");
        list.add("m");
        list.add(1, "b");
        System.out.println(list);
        list.addBefore("m", "l");
        System.out.println(list);
        list.addAfter("c", "h");
        System.out.println(list);
        System.out.println(list.get(2));
        System.out.println(list.indexOf("c"));
        list.replace(2, "B");
        System.out.println(list);
        list.remove(2);
        System.out.println(list);
        list.remove("b");
        System.out.println(list);
    }
}

6.链表实现

6-1.单链表

img

节点,有两部分组成(数据和下一个数据)

public class Node {
    private Object data;
    private Node next;

    public Node() {
    }

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

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }
}

实现类

public class MySingleLinkedList implements IList {

    private Node head = new Node();

    //默认长度是0,头结点不算
    public int size;

    @Override
    public int size() {
        return this.size;
    }

    public MySingleLinkedList() {
    }

    @Override
    public Object get(int i) {
        if (isIndexOutOfBounds(i)) {
            throw new IndexOutOfBoundsException("数组越界异常:" + i);
        }

        Node currentNode = this.head.getNext();
        for (int num = 0; num < this.size; num++) {
            if (num == i) {
                return currentNode.getData();
            } else {
                currentNode = currentNode.getNext();
            }
        }
        return null;
    }

    @Override
    public boolean isEmpty() {
        return this.size == 0;
    }

    @Override
    public boolean contains(Object e) {
        return indexOf(e) > 0;
    }

    @Override
    public int indexOf(Object e) {
        Node currentNode = this.head.getNext();
        for (int num = 0; num < this.size; num++) {
            Object data = currentNode.getData();
            if (data.equals(e)) {
                return num;
            } else {
                currentNode = currentNode.getNext();
            }
        }

        return -1;
    }

    @Override
    public void add(int i, Object e) {
        if (isIndexOutOfBounds(i)) {
            throw new IndexOutOfBoundsException("数组越界异常:" + i);
        }

        // preNode指向head,那么preNode的next作为当前元素
        // 0代表thead的next元素。所以preNode代表的是前一个元素
        Node preNode = this.head;
        for (int num = 0; num < i; num++) {
            preNode = preNode.getNext();
        }

        // 将插入的next指向找到的当前元素的下一个
        // 将要当前元素的next指向要插入的值
        Node currentNextNode = preNode.getNext();
        Node insertNode = new Node(e, currentNextNode);
        preNode.setNext(insertNode);
        this.size++;
    }

    @Override
    public void add(Object e) {
        this.add(this.size, e);
    }

    @Override
    public boolean addBefore(Object obj, Object e) {
        Node preNode = this.head;
        Node currentNode = preNode.getNext();

        for (int num = 0; num < this.size; num++) {
            // 简单起见, obj为null的情况,就不考虑
            if (obj.equals(currentNode.getData())) {
                // 将插入的next指向找到的当前元素
                // 将要前一个的next指向要插入的Node
                Node insertNode = new Node(e, currentNode);
                preNode.setNext(insertNode);
                this.size++;
                return true;
            } else {
                preNode = preNode.getNext();
                currentNode = currentNode.getNext();
            }
        }

        return false;
    }

    @Override
    public boolean addAfter(Object obj, Object e) {
        Node currentNode = this.head.getNext();
        for (int num = 0; num < this.size; num++) {
            // 简单起见, obj为null的情况,就不考虑
            if (obj.equals(currentNode.getData())) {
                // 将插入的next指向找到的当前元素的下一个
                // 将要当前元素的next指向要插入的值
                Node oldNextNode = currentNode.getNext();
                Node insertNode = new Node(e, oldNextNode);
                currentNode.setNext(insertNode);
                this.size++;
                return true;
            } else {
                currentNode = currentNode.getNext();
            }
        }
        return false;
    }

    @Override
    public Object remove(int i) {
        if (isIndexOutOfBounds(i)) {
            throw new IndexOutOfBoundsException("数组越界异常:" + i);
        }

        Node preNode = this.head;
        Node currentNode = preNode.getNext();
        for (int num = 0; num < this.size; num++) {
            // 简单起见, obj为null的情况,就不考虑
            if (i == num) {
                // 将要删node前一个node的next指当删node下个node
                Object result = currentNode.getData();
                preNode.setNext(currentNode.getNext());
                this.size--;
                return result;
            } else {
                preNode = preNode.getNext();
                currentNode = currentNode.getNext();
            }
        }
        return null;
    }

    @Override
    public boolean remove(Object e) {
        Node preNode = this.head;
        Node currentNode = preNode.getNext();
        for (int num = 0; num < this.size; num++) {
            // 简单起见, obj为null的情况,就不考虑
            if (e.equals(currentNode.getData())) {
                // 将要删node前一个node的next指当删node下个node
                preNode.setNext(currentNode.getNext());
                this.size--;
                return true;
            } else {
                preNode = preNode.getNext();
                currentNode = currentNode.getNext();
            }
        }
        return false;
    }

    @Override
    public Object replace(int i, Object e) {
        if (isIndexOutOfBounds(i)) {
            throw new IndexOutOfBoundsException("数组越界异常:" + i);
        }

        Node currentNode = this.head.getNext();
        for (int num = 0; num < this.size; num++) {
            if (i == num) {
                currentNode.setData(e);
                return e;
            } else {
                currentNode = currentNode.getNext();
            }
        }
        return null;
    }

    private boolean isIndexOutOfBounds(int index) {
        return index < 0 || index > this.size;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder("[");
        Node p = head.getNext();
        while (p != null) {
            //取出结点值
            Object data = p.getData();
            //加入StringBuffer
            builder.append(data + ",");
            //后移一个结点
            p = p.getNext();
        }
        //删除最后的一个逗号
        if (builder.length() > 1) {
            builder.deleteCharAt(builder.length() - 1);
        }
        builder.append("]");

        return builder.toString();
    }
}

测试类

public class MySingleLinkedListTest {

    public static void main(String[] args) {
        IList list = new MySingleLinkedList();
        System.out.println(list.isEmpty());
        list.add("a");
        System.out.println(list.isEmpty());
        list.add("c");
        list.add("m");
        System.out.println(list);
        list.add(1, "b");
        System.out.println(list);
        list.addBefore("m", "l");
        System.out.println(list);
        list.addAfter("c", "h");
        System.out.println(list);
        System.out.println(list.get(2));
        System.out.println(list.indexOf("c"));
        list.replace(2, "B");
        System.out.println(list);
        list.remove(2);
        System.out.println(list);
        list.remove("b");
        System.out.println(list);
    }
}

6-2.双向列表

单链表的一个优点是结构简单,但是它也有一个缺点,即在单链表中只能通过一个结点的引用访问其后续结点,而无法直接访问其前驱结点,

img

双向链表是通过上述定义的结点使用 pre 以及 next 域依次串联在一起而形成的。一个双向链表的结构如图所示。

img

6-3.循环链表

单向链表的循环

img

双向链表的循环

img

7.进入一步深造

下一步如何再深造呢?

去看java的ArrayListLinkedList,有了前面的基础,相信源码阅读就so easy 了。

8.推荐

能读到文章最后,首先得谢谢您对本文的肯定,你的肯定是对博主最大的鼓励。

你觉本文有帮助,那就点个👍
你有疑问,那就留下您的💬
怕把我弄丢了,那就把我⭐
电脑不方便看,那就把发到你📲

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值