@Override
  public Map<TConsensusGroupId, TDataNodeConfiguration> removeNodeReplicaSelect(
      Map<Integer, TDataNodeConfiguration> availableDataNodeMap,
      Map<Integer, Double> freeDiskSpaceMap,
      List<TRegionReplicaSet> allocatedRegionGroups,
      Map<TConsensusGroupId, String> regionDatabaseMap,
      Map<String, List<TRegionReplicaSet>> databaseAllocatedRegionGroupMap,
      Map<TConsensusGroupId, TRegionReplicaSet> remainReplicasMap) {
    try {
      // 1. prepare: compute regionCounter, databaseRegionCounter, and combinationCounter

      prepare(availableDataNodeMap, allocatedRegionGroups, Collections.emptyList());
      computeInitialDbLoad(databaseAllocatedRegionGroupMap);

      // 2. Build allowed candidate set for each region that needs to be migrated.
      // For each region in remainReplicasMap, the candidate destination nodes are all nodes in
      // availableDataNodeMap
      // excluding those already in the remain replica set.
      List<TConsensusGroupId> regionKeys = new ArrayList<>(remainReplicasMap.keySet());
      allowedCandidatesMap = new HashMap<>();
      this.regionDatabaseMap = regionDatabaseMap;
      for (TConsensusGroupId regionId : regionKeys) {
        TRegionReplicaSet remainReplicaSet = remainReplicasMap.get(regionId);
        Set<Integer> notAllowedNodes =
            remainReplicaSet.getDataNodeLocations().stream()
                .map(TDataNodeLocation::getDataNodeId)
                .collect(Collectors.toSet());

        // Allowed candidates are the nodes not in the exclusion set
        List<Integer> candidates =
            availableDataNodeMap.keySet().stream()
                .filter(nodeId -> !notAllowedNodes.contains(nodeId))
                .sorted(
                    (a, b) -> {
                      int cmp = Integer.compare(regionCounter[a], regionCounter[b]);
                      return (cmp != 0)
                          ? cmp
                          : Integer.compare(databaseRegionCounter[a], databaseRegionCounter[b]);
                    })
                .collect(Collectors.toList());
        Collections.shuffle(candidates);

        // Sort candidates in ascending order of current global load (regionCounter)
        allowedCandidatesMap.put(regionId, candidates);
      }

      // Optionally, sort regionKeys by the size of its candidate list (smaller candidate sets
      // first)
      regionKeys.sort(Comparator.comparingInt(id -> allowedCandidatesMap.get(id).size()));

      // 3. Batch DFS
      Map<TConsensusGroupId, TDataNodeConfiguration> result = new HashMap<>();

      for (int start = 0; start < regionKeys.size(); start += BATCH_SIZE) {
        dfsRegionKeys = regionKeys.subList(start, Math.min(start + BATCH_SIZE, regionKeys.size()));
        int batchSize = dfsRegionKeys.size();

        // Initialize buffer
        bestAssignment = new int[batchSize];
        // bestMetrics holds the best found metrics: [maxGlobalLoad, maxDatabaseLoad, scatterValue].
        bestMetrics = new int[] {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE};
        // currentAssignment holds the candidate nodeId chosen for the region at that index
        int[] currentAssignment = new int[batchSize];
        // additionalLoad holds the number of extra regions assigned to each node in this migration
        // solution.
        int[] additionalLoad = new int[regionCounter.length];

        scatterDelta = new int[batchSize][regionCounter.length];
        for (int r = 0; r < batchSize; r++) {
          TConsensusGroupId regionId = dfsRegionKeys.get(r);
          for (int nodeId : allowedCandidatesMap.get(regionId)) {
            int inc = 0;
            for (TDataNodeLocation loc : remainReplicasMap.get(regionId).getDataNodeLocations()) {
              inc += combinationCounter[nodeId][loc.getDataNodeId()];
            }
            scatterDelta[r][nodeId] = inc;
          }
        }

        int currentMaxGlobalLoad = 0;
        for (int nodeId = 0; nodeId < regionCounter.length; nodeId++) {
          currentMaxGlobalLoad = Math.max(currentMaxGlobalLoad, regionCounter[nodeId]);
        }

        dfsRemoveNodeReplica(0, currentMaxGlobalLoad, 0, currentAssignment, additionalLoad);

        if (bestMetrics[0] == Integer.MAX_VALUE) {
          // This should not happen if there is at least one valid assignment
          return Collections.emptyMap();
        }

        for (int i = 0; i < batchSize; i++) {
          TConsensusGroupId regionId = dfsRegionKeys.get(i);
          int chosenNodeId = bestAssignment[i];
          result.put(regionId, availableDataNodeMap.get(chosenNodeId));

          regionCounter[chosenNodeId]++;
          String db = regionDatabaseMap.get(regionId);
          if (db != null) {
            int[] dbLoad = initialDbLoad.computeIfAbsent(db, k -> new int[regionCounter.length]);
            dbLoad[chosenNodeId]++;
          }
          for (TDataNodeLocation loc : remainReplicasMap.get(regionId).getDataNodeLocations()) {
            combinationCounter[chosenNodeId][loc.getDataNodeId()]++;
            combinationCounter[loc.getDataNodeId()][chosenNodeId]++;
          }
        }
      }
      return result;
    } finally {
      // Clear any temporary state to avoid impacting subsequent calls
      clear();
    }
  }
Allocate Region through Greedy and CopySet Algorithm.
iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/region/GreedyCopySetRegionGroupAllocator.java