海量积分排名 简化后的具体实现

首先积分值有个最大值,积分由n变化到m 排名变化的只是在积分n到积分m的客户排名会发生变化故此 有如下实现
使用RankAndNum来表示处在该积分的排名和数值
使用RankAndNums数值来保存所有积分排名(注意:RankAndNums[n]中RankAndNum中的rank和Num则表示积分为n的排名和处在积分为n的用户数量)
看下面积分改变时注意自己画图理解,积分由n上升到m时位于n到m(不包括m)中积分的排名(如果该位置已经有排名)都会降低一位;
其他情况也是类似
下面上代码:
/**
 * 该类主要用于现有积分排名策略 能够迅速感知积分排名的变化;
 * 而时间复杂度又不至于过高。
 * <p>
 * 该算法在计算时需要考虑变化之后,原来位置有没有积分排名,变化后原位置是否有积分排名,
 * 是否应该清楚该积分排名等操作。是不是首位,末尾等提示。
 */
public class paiming {

    private int MAX_SCORE = 10; //默认100
    private RankAndNum[] rankAndNums = new RankAndNum[MAX_SCORE];

    //初始化rank 
    public void init() {
        for (int i = 0; i < rankAndNums.length; i++) {
            rankAndNums[i] = new RankAndNum(0, 0);
        }
    }

    public static void main(String[] args) {
        paiming p = new paiming();
        int[] array = new int[]{1, 7, 3, 5, 9, 2, 8}; 
    array =p.getArray(array);//排序获得有序的数组 使用的是堆排序,
     p.init();  //给现有的积分进行排名
        p.getRanking(array);
        p.getRecentRanking(7, 0);
        for (int i = 0; i < 10; i++) {
            System.out.print(p.rankAndNums[i].rank);
            System.out.println(p.rankAndNums[i].Num);
        }
     
         
       
        //int t = p.find(array, 0, array.length - 1, 7);
        //System.out.print(t);
    }

    //首先进行排序 从0开始为根节点,
    public void minHeap(int[] array, int start, int end) {
        int j = 2 * start + 1; //左做节点
        int tmp = array[start];
        int length = end;
        while (j <= length) {
            if (j + 1 <= length && array[j + 1] <array[j]) {
                j++;
            }
            if (array[j] < tmp) {
                array[start] = array[j];
                start = j;
                j = 2 * start + 1;
            } else {
                break;
            }
        }
        array[start] = tmp;
    }
//构建最小堆

    /**
     * 使用的是堆排序,每次出最小放在数组末端,因此t表示未处理的最后下标
     *
     * @param array 目标数组
     * @param end   为处理数据的最后下标
     */
    public void heapAdjust(int[] array, int end) {
        for (int j = end / 2; j >= 0; j--) {
            minHeap(array, j, end);
        }
    }

    public int[] getArray(int[] array) {
        for (int i = array.length - 1; i > 0; i--) {
            heapAdjust(array, i);
            swap(array, 0, i);
        }
        return array;
    }

    public void swap(int[] array, int i, int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

    //获得积分排名 使用rank来保存 并(存在分数相同的情况),因为已经有序 但注意到积分可能会相同
    //这个时候取前面的数组下表为积分排名
    public void getRanking(int[] array) {
        for (int i = 0, j = 0; i < array.length; i++) {
            if (i - 1 > 0 && array[i - 1] == array[i]) {
                rankAndNums[array[i - 1]].Num++;
                continue;
            }
            rankAndNums[array[i]].rank = i + 1;
            rankAndNums[array[i]].Num = 1;
        }

    }

    //假设积分发生变动,某用户由n积分 ,变为 m积分,m,n都为正整数;
    public void getRecentRanking(int n, int m) {
        rankAndNums[n].Num -= 1; //变更后对应积分数量减一
        rankAndNums[m].Num += 1; //变更后对应积分数量加一
        if (m > n) {  //积分增加

            if (rankAndNums[m].rank == 0) { //若变更后的位置没有排名
                int next = -1; //判断变更后的前一个积分排名,若有则会指向他 没有则为-1,默认没有
                for (int i = n; i < m; i++) {
                    if (rankAndNums[i].Num != 0) {
                        next = i;
                        rankAndNums[i].rank += 1;
                    }
                }
                if (next == -1) { //当pervious=-1是说明他们间的积分全都没有排名,
                    rankAndNums[m].rank = rankAndNums[n].rank;
                } else {
                    rankAndNums[m].rank = rankAndNums[next].rank - 1;
                }
            } else { //变更后原来位置本来就有人
                for (int i = n; i < m; i++) {
                    if (rankAndNums[i].Num != 0) {
                        rankAndNums[i].rank += 1;
                    }
                }

            }
            if (rankAndNums[n].Num == 0) {
                rankAndNums[n].rank = 0;
            }

        } else if (m < n) { //积分减少

                int previous = -1; //判断前一个是否有排名积分
                for (int i = n - 1; i >= m; i--) {
                    if (rankAndNums[i].rank != 0) { //这里注意别用Num属性值判断 应为变更后m出不管有没有
                        //Num数值都是大于0的;
                        previous = i;
                        rankAndNums[i].rank -= 1;
                    }
                }
                //对排名m另做处理

                if (previous == -1) {//m到n之间别的积分没占排名
                    if (rankAndNums[n].Num != 0) { //n位置不为空
                        rankAndNums[m].rank = rankAndNums[n].rank + rankAndNums[n].Num;
                    } else { //n位置为空
                        rankAndNums[m].rank = rankAndNums[n].rank;
                    }
                } else if(previous == m){

                }else {
                    rankAndNums[m].rank = rankAndNums[previous].rank + rankAndNums[previous].Num;
                }

            if (rankAndNums[n].Num == 0) { // 原先位置没有了人 不该有排名
                rankAndNums[n].rank = 0;
            }
        } else {
            //积分没变
            return;
        }


    }

    // (递归查找有没有)

    /**
     * 本函数主要用于查找已在array中的元素,不存在查找不到的情况
     *
     * @param array  目标数组
     * @param left   起始位置
     * @param right  终止位置
     * @param target 查找对象
     * @return 排名
     */
    private int find(int array[], int left, int right, int target) {
        int pos = (left + right) / 2;
        if (right >= left) {
            if (target > array[pos]) {
                return find(array, pos + 1, right, target);
            } else if (target < array[pos]) {
                return find(array, left, pos - 1, target);
            } else {
                return pos;
            }
        }
        return -1;

    }
    //查找最后一个大于自己的数的下标即为现在排名


}

猜你喜欢

转载自blog.csdn.net/qq_32459653/article/details/81089625
今日推荐