『算法』顺序表和单向不带头非循环链表中两种取巧的删除算法

我们知道顺序表中删除中间元素的时间复杂度是 O ( N ) O(N) O(N),单向不带头非循环链表中如果知道想删除一个结点,我们必须知道这个结点的前一个节点,否则的话,时间复杂度也是 O ( N ) O(N) O(N),本片博客介绍两种取巧的删除方法。

顺序表中删除元素

  • 我们知道,顺序表中删除中间元素,都是将后面的元素向前移动一个位置,来覆盖要删除的元素,这里,我们可以采用一种更取巧的方法来删除中间元素。
  • 如果我们要删除顺序表中指定下标的元素,我们可以将要删除的元素和最后一个元素的值进行交换,然后,将顺序表的size减1即可。这时候加粗样式时间复杂度就是 O ( 1 ) O(1) O(1)
  • 但是这种方法会破坏顺序表中元素的顺序,所以不推荐使用,但是在一些特殊场景可以考虑这种思想

下面看代码,我们实现一个简单的顺序表

public class SeqList {
    private int[] arr = new int[10];
    private int size = 0;

    // 删除指定下标元素
    public void remove(int index) {
        if (index < 0 || index >= this.size) {
            return;
        }

        swap(this.size - 1, index);
        --this.size;
    }

    private void swap(int i, int j) {
        int temp = this.arr[i];
        this.arr[i] = this.arr[j];
        this.arr[j] = temp;
    }

    // 简单的插入用来演示,暂时不考虑扩容问题
    public void add(int index, int val) {
        if (index < 0 || index > this.size) {
            return;
        }

        for (int i = this.size; i > index; --i) {
            this.arr[i] = this.arr[i - 1];
        }
        this.arr[index] = val;
        ++this.size;
    }

    public void display() {
        System.out.print("[");
        for (int i = 0; i < this.size; ++i) {
            System.out.print(this.arr[i]);
            if (i == this.size - 1) {
                break;
            }
            System.out.print(", ");
        }
        System.out.println("]");
    }
}

下面,我们写一个测试代码

public class Test {
    public static void main(String[] args) {
        SeqList list = new SeqList();

        list.add(0, 11);
        list.add(1, 12);
        list.add(2, 13);
        list.add(3, 14);
        list.add(4, 15);
        list.display();

        // 删除下标为1的元素
        list.remove(1);
        list.display();
    }
}

在这里插入图片描述
在这里插入图片描述


单向不带头非循环链表删除结点

  • 对于单向不带头非循环链表来说,如果想要删除中间的某个节点,如果我们知道这个结点的前一个节点,那么删除操作的时间复杂度就是 O ( 1 ) O(1) O(1)
  • 但是如果我们知道要删除结点的引用,我们必须去遍历链表,找到该节点的前一个节点,就可以删除该结点,但是这时候,时间复杂度就是 O ( N ) O(N) O(N)
  • 其实我们也可以以 O ( 1 ) O(1) O(1)的时间复杂度来删除结点,我们将当前结点的值拷贝到下一个结点,然后去删除当前结点的下一个结点,这时候时间复杂度就是 O ( 1 ) O(1) O(1)这种方法不适用于删除最后一个结点

下面看代码,我们来实现一个简单的链表。

public class LinkedList {
    private Node head = null;

    // 根据一个结点的引用,删除该节点
    public void remove(Node node) {
        if (node == null || node.next == null) {
            return;
        }

        node.val = node.next.val;
        node.next = node.next.next;
    }

    // 写一个头插来方便演示
    public void addFirst(int val) {
        Node node = new Node(val);
        node.next = this.head;
        this.head = node;
    }

    // 写一个获取结点引用的方法,便于演示
    public Node search(int val) {
        for (Node cur = this.head; cur != null; cur = cur.next) {
            if (cur.val == val) {
                return cur;
            }
        }
        return null;
    }

    public void display() {
        System.out.print("[");
        for (Node cur = this.head; cur != null; cur = cur.next) {
            System.out.print(cur.val);
            System.out.print(" -> ");
        }
        System.out.println("null]");
    }
}

class Node {
    public int val;
    public Node next = null;

    public Node(int val) {
        this.val = val;
    }
}

我们来写一段测试代码来验证一下

public class Test {
    public static void main(String[] args) {
        LinkedList list = new LinkedList();

        list.addFirst(5);
        list.addFirst(4);
        list.addFirst(3);
        list.addFirst(2);
        list.addFirst(1);
        list.display();

        Node toRemove = list.search(3);
        list.remove(toRemove);
        list.display();
    }
}

在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值