實時插入排序算法

背景

在正常的工做中,咱們常常遇到從消息隊列中接收一天的數據量,並對其進行排序的場景。那麼,咱們一般會想到最經典的十大經典排序算法。確實,這些算法均可以知足咱們的場景須要,,但若是咱們要求接收消息過程當中,實時進行排序呢?這些算法顯然都不能知足需求,它們都是在接收到全部數據後,再統一排序。因此,今天,咱們動手本身遍寫一個實時插入排序算法!html

理論

參考插入排序算法,咱們將接收到的數據存放在一個鏈表結構當中,每接收一個新數據,就讓它與已存在的數據逐個比較,找到須要插入的位置下一個節點,執行新節點插入操做!java

圖例

代碼實現

1. 比較器

由於要進行排序,因此咱們須要先創建一個比較器接口,來保證用戶自定義比較方法。node

package cn.wxson.sort.comparator;

import cn.wxson.sort.node.AbstractNode;
import com.sun.istack.internal.NotNull;

/**
 * Title 比較器
 *
 * @author Ason(18078490)
 * @date 2020-08-03
 */
@FunctionalInterface
public interface AbstractComparator<T> {

    /**
     * 比較方法
     *
     * @param one 數據節點一
     * @param two 數據節點二
     * @return 比較結果
     */
    boolean compare(@NotNull AbstractNode<T> one, @NotNull AbstractNode<T> two);
}

2. 節點

2.1 節點抽象類

建立節點抽象類,存放數據,並利用比較器實現比較邏輯。算法

package cn.wxson.sort.node;

import cn.wxson.sort.comparator.AbstractComparator;
import lombok.Getter;
import lombok.Setter;

/**
 * Title 節點
 *
 * @author Ason(18078490)
 * @date 2020-08-03
 */
@Setter
@Getter
public abstract class AbstractNode<T> {

    /**
     * 前一個節點
     */
    private AbstractNode<T> pre;
    /**
     * 後一個節點
     */
    private AbstractNode<T> next;
    /**
     * 節點內存儲的實際數據
     */
    protected T t;

    /**
     * 無參構造
     */
    public AbstractNode() {
        super();
    }

    /**
     * 帶參構造
     *
     * @param t 數據對象
     */
    public AbstractNode(T t) {
        this.t = t;
    }

    /**
     * 獲取數據比較器
     *
     * @return 比較器對象
     */
    protected abstract AbstractComparator<T> comparator();

    /**
     * 數據比較
     *
     * @param data 數據節點
     */
    public AbstractNode<T> compareTo(AbstractNode<T> data) {
        return this.comparator().compare(this, data) ? this : this.next.compareTo(data);
    }
}

2.2 虛擬頭節點

頭節點是一個虛擬節點,只作傳遞給下個節點操做,自身實現比較器,數據比較時永遠排在第一位。json

package cn.wxson.sort.node;

import cn.wxson.sort.comparator.AbstractComparator;

/**
 * Title   虛擬頭節點
 * 只作傳遞給下個節點操做,數據比較永遠排在第一位
 *
 * @author Ason(18078490)
 * @date 2020-08-03
 */
public final class DummyHeadNode<T> extends AbstractNode<T> implements AbstractComparator<T> {

    /**
     * 獲取數據比較器
     *
     * @return 比較器對象
     */
    @Override
    protected AbstractComparator<T> comparator() {
        return this;
    }

    /**
     * 比較方法
     * 頭節點永遠排在第一位
     *
     * @param one 節點一
     * @param two 節點二
     * @return 比較結果
     */
    @Override
    public boolean compare(AbstractNode<T> one, AbstractNode<T> two) {
        return false;
    }
}

2.3 虛擬尾節點

尾節點也是一個虛擬節點,不須要作任務傳遞操做,自身實現比較器,排序時永遠排在鏈表最後一位。數據結構

package cn.wxson.sort.node;

import cn.wxson.sort.comparator.AbstractComparator;

/**
 * Title 虛擬尾節點
 * 由於是最後一個節點,因此,不須要作任務傳遞操做,永遠排在鏈表最後一位
 *
 * @author Ason(18078490)
 * @date 2020-08-03
 */
public final class DummyTailNode<T> extends AbstractNode<T> implements AbstractComparator<T> {

    /**
     * 獲取數據比較器
     *
     * @return 比較器對象
     */
    @Override
    protected AbstractComparator<T> comparator() {
        return this;
    }

    /**
     * 比較方法
     * 尾節點永遠排在最後一位
     *
     * @param one 節點一
     * @param two 節點二
     * @return 比較結果
     */
    @Override
    public boolean compare(AbstractNode<T> one, AbstractNode<T> two) {
        return true;
    }
}

2.4 普通節點

普通節點將接收用戶自定義比較器進行數據比較。app

package cn.wxson.sort.node;

import cn.wxson.sort.comparator.AbstractComparator;

/**
 * Title 普通節點
 *
 * @author Ason(18078490)
 * @date 2020-08-03
 */
public class CommonNode<T> extends AbstractNode<T> {

    /**
     * 比較器
     * 是普通節點必須的比較工具
     */
    private AbstractComparator<T> comparator;

    /**
     * 帶參構造
     *
     * @param data 數據
     */
    public CommonNode(T data) {
        super(data);
    }

    /**
     * 註冊比較器
     *
     * @param comparator 比較器
     */
    public void register(AbstractComparator<T> comparator) {
        this.comparator = comparator;
    }

    /**
     * 獲取數據比較器
     *
     * @return 比較器對象
     */
    @Override
    protected AbstractComparator<T> comparator() {
        return this.comparator;
    }
}

3. 鏈表

3.1 鏈表結構抽象類

鏈表結構抽象類中存在虛擬頭節點和虛擬尾節點,並實現鏈表全部遍歷功能。ide

package cn.wxson.sort.biz;

import cn.wxson.sort.node.AbstractNode;
import cn.wxson.sort.node.DummyHeadNode;
import cn.wxson.sort.node.DummyTailNode;
import com.google.common.collect.Lists;

import java.util.List;

/**
 * Title 鏈表結構抽象類
 * 作初始化鏈表、表元素遍歷等操做
 *
 * @author Ason(18078490)
 * @date 2020-08-03
 */
public class AbstractLinkedList<T> {

    /**
     * 虛擬頭節點
     */
    protected final AbstractNode<T> dummyHeadNode;
    /**
     * 虛擬尾節點
     */
    protected final AbstractNode<T> dummyTailNode;

    /**
     * 無參構造
     * 初始化鏈表的頭尾虛擬節點
     */
    public AbstractLinkedList() {
        // 建立虛擬頭節點、虛擬尾節點,並將它們關聯起來
        this.dummyHeadNode = new DummyHeadNode<>();
        this.dummyTailNode = new DummyTailNode<>();
        this.dummyHeadNode.setNext(this.dummyTailNode);
        this.dummyTailNode.setPre(this.dummyHeadNode);
    }

    /**
     * 除虛擬頭節點、虛擬尾節點外,是否包含其餘節點
     *
     * @return 斷定結果
     */
    public boolean hasNext() {
        return this.dummyHeadNode.getNext() != this.dummyTailNode;
    }

    /**
     * 獲取鏈表元素
     *
     * @return 元素集合
     */
    public List<T> list() {
        List<T> result = Lists.newLinkedList();
        AbstractNode<T> pos = this.dummyHeadNode.getNext();
        while (pos != this.dummyTailNode) {
            result.add(pos.getT());
            pos = pos.getNext();
        }
        return result;
    }

    /**
     * toString方法
     *
     * @return 字符串
     */
    @Override
    public String toString() {
        // 不存在元素時,返回空集合
        if (!hasNext()) {
            return "[]";
        }
        // 存在元素時,逐個打印
        StringBuilder sb = new StringBuilder("[");
        AbstractNode<T> pos = this.dummyHeadNode.getNext();
        while (pos != this.dummyTailNode) {
            sb.append(pos.getT().toString()).append(",");
            pos = pos.getNext();
        }
        String result = sb.substring(0, sb.lastIndexOf(","));
        return result + "]";
    }
}

3.2 鏈表結構類

鏈表結構類中,在新增元素時,註冊比較器,進行比較後,實現實時插入操做。工具

package cn.wxson.sort.biz;

import cn.wxson.sort.comparator.AbstractComparator;
import cn.wxson.sort.node.AbstractNode;
import cn.wxson.sort.node.CommonNode;

/**
 * Title 鏈表結構類
 * 功能:從頭結點向後進行比較
 *
 * @author Ason(18078490)
 * @date 2020-08-03
 */
public class LinkedList<T> extends AbstractLinkedList<T> {

    /**
     * 比較器
     */
    private final AbstractComparator<T> comparator;

    /**
     * 帶參構造
     *
     * @param comparator 比較器
     */
    public LinkedList(AbstractComparator<T> comparator) {
        // 初始化鏈表
        super();
        // 注入比較器
        this.comparator = comparator;
    }

    /**
     * 新增元素
     *
     * @param data 數據
     */
    public void add(T data) {
        // 建立新節點,並註冊比較器
        CommonNode<T> newNode = new CommonNode<>(data);
        newNode.register(this.comparator);
        // 從頭節點開始,找到新節點應該插入的位置的下一個節點
        AbstractNode<T> next = dummyHeadNode.compareTo(newNode);
        // 將新節點插入鏈表
        AbstractNode<T> pre = next.getPre();
        newNode.setPre(pre);
        newNode.setNext(next);
        pre.setNext(newNode);
        next.setPre(newNode);
    }
}

4. 測試

咱們新建幾個用戶類,來根據用戶年齡進行實時排序測試。學習

4.1 用戶類

package cn.wxson.sort.test;

import com.alibaba.fastjson.JSON;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

/**
 * Title 用戶類
 *
 * @author Ason(18078490)
 * @date 2020-08-03
 */
@Getter
@Setter
@Builder
public class User {

    private String name;
    private int age;

    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }
}

4.2 測試類

package cn.wxson.sort.test;

import cn.wxson.sort.biz.LinkedList;
import lombok.extern.slf4j.Slf4j;

/**
 * Title 測試類
 *
 * @author Ason(18078490)
 * @date 2020-08-03
 */
@Slf4j
public class Domain {

    public static void main(String[] arg) {
        // 建立三個用戶
        User tom = User.builder().name("Tom").age(20).build();
        User kate = User.builder().name("kate").age(18).build();
        User jerry = User.builder().name("Jerry").age(22).build();
        // 建立鏈表,放入按照用戶年齡升序排列的比較器
        LinkedList<User> linkedList = new LinkedList<>((one, two) -> one.getT().getAge() >= two.getT().getAge());
        log.info("鏈表初始化:{}", linkedList.toString());
        linkedList.add(tom);
        log.info("接收到第一個用戶:Tom,{}", linkedList.toString());
        linkedList.add(kate);
        log.info("接收到第一個用戶:Kate,{}", linkedList.toString());
        linkedList.add(jerry);
        log.info("接收到第一個用戶:Jerry,{}", linkedList.toString());
    }
}

執行以上測試樣例,咱們來看下每次插入新數據後,鏈表結構:

執行結果

經過上面結果,咱們能夠看到,實時插入效果已經實現!

總結

本篇文章經過鏈表結構實現了實時插入排序的功能,它的時間複雜度爲O(n),排序執行時間,隨着數據量遞增。

這篇文章數據結構與我以前的一篇博文(基於隊列模型編寫一個入崗檢查站)是相通的,學習後,但願對你有幫助!

相關文章
相關標籤/搜索