清除整数数组中任意和为0的子串并输出

输入一个整数序列. 每个项可能的取值为 正数,负数,零. 如 {1,3,4,-3,-1,9}

要求去除数列中的任意长度的和为零的子串.比如上述序列中的. 4,-3,-1

package train.interview;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 处理思路;
 *  1. 先生成一个前缀和序列.
 *     如 S1,S2,S3,S4.....Sn,Sn+1 
 *      这样新的序列中如果存在两相等的值. 比如 S2=S4.那 a3+a4 = 0
 *  问题转化为查找前缀和中的数组中的重复出现的sum 的数.  
 *  这只是思路.具体细节要考虑一个值出现不只2次.比如3次的时候该如何处理. 这种
 *  这里的处理是把出现的首尾连在一起作为一个0序列.
 *  在零序列消除后.0序列中已经擦除的数可以认为不存在.从而新形成了一个前缀和 序列.这样.就可以重复的往下.
 *  直到完成所有的项的检查.具体看代码
 */
public class TrimZeroSubSequence {

    private class PrefixSumNode{

        // 当前节点的值
        private Integer value = null;
        // 当前节点的前缀和
        private Integer prefixSum = null;
        // 当前节点的索引号
        private int index = -1;

        // 把节点串起来的指针
        // 前向指针
        private PrefixSumNode prevNode = null;
        // 后向指针
        private PrefixSumNode nextNode = null;

        public Integer getValue() {
            return value;
        }

        public void setValue(Integer value) {
            this.value = value;
        }

        public Integer getPrefixSum() {
            return prefixSum;
        }

        public void setPrefixSum(Integer prefixSum) {
            this.prefixSum = prefixSum;
        }

        public int getIndex() {
            return index;
        }

        public void setIndex(int index) {
            this.index = index;
        }

        public PrefixSumNode getPrevNode() {
            return prevNode;
        }

        public void setPrevNode(PrefixSumNode prevNode) {
            this.prevNode = prevNode;
        }

        public PrefixSumNode getNextNode() {
            return nextNode;
        }

        public void setNextNode(PrefixSumNode nextNode) {
            this.nextNode = nextNode;
        }

        @Override
        public String toString() {
            return "PrefixSumNode{" +
                "value=" + value +
                ", prefixSum=" + prefixSum +
                ", index=" + index +
                '}';
        }

    }

    public List<Integer> filterZeroSubSequence(List<Integer> source) {

        if (source == null || source.isEmpty()) {
            return null;
        }
        if (source.size() == 1 && source.get(0) == 0) {
            return null;
        }

        PrefixSumNode headNode = null, tailNode = null;

        int curSum = 0;
        int curIndex = 0;

        // 构建前缀和双向链表: O(n)
        for (Integer value : source) {
            curSum += value;

            PrefixSumNode node = new PrefixSumNode();
            node.setIndex(curIndex++);
            node.setPrefixSum(curSum);
            node.setValue(value);

            if (tailNode != null) {
                tailNode.setNextNode(node);
                node.setPrevNode(tailNode);
                tailNode = node;
            }else {
                headNode = node;
                tailNode = node;
            }
        }

        // 存储前缀和已经存储的值
        Set<Integer> existPrefixSumSet = new HashSet<>();
        // 存储前缀和与节点的 mapping 关系
        Map<Integer/*preSum*/, PrefixSumNode> sum2NodeMapping = new HashMap<>();


        // 大循环 O(n) * 2
        PrefixSumNode iterNode = headNode;
        do {

            if (!existPrefixSumSet.contains(iterNode.prefixSum)) {
                System.out.println("添加不存在的节点:" + iterNode);
                existPrefixSumSet.add(iterNode.prefixSum);
                sum2NodeMapping.put(iterNode.getPrefixSum(), iterNode);
                System.out.println("put:" + iterNode.getPrefixSum());
            } else {
                // 已经存在此前缀和.要进行合并清除.
                // 1.找出先前存在的节点
                System.out.println("已经存在:"+iterNode.getPrefixSum());
                PrefixSumNode existNode = sum2NodeMapping.get(iterNode.prefixSum);
                if (existNode == null) {
                    System.out.println("NotExist:" + iterNode.prefixSum);
                    return null;
                }
                try {
                    cleanZeroSequence(existNode, iterNode, existPrefixSumSet, sum2NodeMapping);
                } catch (Exception e) {
                    System.out.println(e);
                }
            }

        } while ((iterNode = iterNode.nextNode) != null);

        List<Integer> outPut = new LinkedList<>();

        iterNode = headNode;

        do {
            outPut.add(iterNode.getValue());
        } while ((iterNode = iterNode.nextNode) != null);

        return outPut;
    }


    /**
     * 清除两节点间的子序列
     * @param existNode
     * @param iterNode
     * @param existPrefixSumSet
     * @param sum2NodeMapping
     */
    private void cleanZeroSequence(PrefixSumNode existNode, PrefixSumNode iterNode,
                                   Set<Integer> existPrefixSumSet,
                                   Map<Integer, PrefixSumNode> sum2NodeMapping) {


        PrefixSumNode nextNode = existNode.nextNode;
        do {
            sum2NodeMapping.remove(nextNode.getPrefixSum());
            existPrefixSumSet.remove(nextNode.getPrefixSum());
            System.out.println("remove:" + nextNode.getPrefixSum());
        } while (nextNode != iterNode && (nextNode = nextNode.nextNode) != null);

        // 链表中把0子序列全部清除
        existNode.setNextNode(iterNode.nextNode);

        if (iterNode.nextNode != null) {
            iterNode.nextNode.prevNode = existNode;
        }
    }

    public static void main(String[] args) {
        List<Integer> list = new TrimZeroSubSequence()
            .filterZeroSubSequence(Arrays.asList(3, 5, -3, -1, -1, 2, 8, 9, -2, -7, 23));
        System.out.println(list);
    }
}

  

程序中的示例串为:

(3, 5, -3, -1, -1, 2, 8, 9, -2, -7, 23)
-------------- ---------

输出为:

[3, 2, 8, 23] 

 

 

 

 

posted @ 2020-10-15 01:03  firfor  阅读(123)  评论(0编辑  收藏  举报