# 5 Best Ways to Check if an Array Can Be Sorted with Constrained Swaps in Python

Rate this post

π‘ Problem Formulation: You have an array and a set of index pairs within which you can swap elements. The challenge is to determine if the array can be sorted into non-decreasing order by only using swaps between these given index pairs. For instance, given `array = [1, 3, 5, 2, 4]` and `swaps = [(1, 3), (2, 4)]`, we want to know if we can sort the array by only swapping elements at indices 1 with 3 and 2 with 4.

## Method 1: Using Union-Find

The Union-Find algorithm is an efficient method for tracking a set of elements partitioned into a number of disjoint (non-overlapping) subsets. By creating groups with the provided index pairs, we can check if swapping within groups can sort the array. For each disjoint set, the elements need to be able to form a sorted sequence when placed at their respective indices.

Here’s an example:

```def find(x, parent):
if parent[x] != x:
parent[x] = find(parent[x], parent)
return parent[x]

def union(x, y, parent, rank):
xroot = find(x, parent)
yroot = find(y, parent)
if xroot != yroot:
if rank[xroot]  rank[yroot]:
parent[yroot] = xroot
else:
parent[yroot] = xroot
rank[xroot] += 1

def can_sort_with_swaps(arr, swaps):
n = len(arr)
parent = list(range(n))
rank = [0] * n
for x, y in swaps:
union(x, y, parent, rank)
groups = {}
for i in range(n):
grp = find(i, parent)
if grp not in groups:
groups[grp] = []
groups[grp].append(arr[i])
for grp in groups:
groups[grp].sort()
sorted_arr = []
for i in range(n):
grp = find(i, parent)
sorted_arr.append(groups[grp].pop(0))
return sorted_arr == sorted(arr)

array = [1, 3, 5, 2, 4]
swap_pairs = [(1, 3), (2, 4)]
print(can_sort_with_swaps(array, swap_pairs))
```

Output: `True`

This code snippet defines helper functions for find and union operations used in the Union-Find algorithm. Then, a function `can_sort_with_swaps()` checks if the given array is sortable with the specified swaps. Disjoint subsets are created based on the swaps. Each subset is sorted, then we check if the generated sequence matches the sorted array.

## Method 2: Graph Connectivity

By representing the array as a graph where indices are nodes and swaps as edges, we can explore the graph’s connectivity. If all elements in a connected component can be sorted within themselves, then the whole array can be sorted. We can perform a Depth-First Search (DFS) or Breadth-First Search (BFS) to find connected components.

Here’s an example:

```from collections import defaultdict

def can_sort_with_swaps(arr, swaps):
def dfs(node, index_group, value_group):
visited[node] = True
index_group.append(node)
value_group.append(arr[node])
for nei in graph[node]:
if not visited[nei]:
dfs(nei, index_group, value_group)

n = len(arr)
graph = defaultdict(list)
for x, y in swaps:
graph[x].append(y)
graph[y].append(x)

visited = [False] * n
for node in range(n):
if not visited[node]:
index_group, value_group = [], []
dfs(node, index_group, value_group)
index_group.sort()
value_group.sort()
for idx, val in zip(index_group, value_group):
arr[idx] = val
return all(arr[i] <= arr[i+1] for i in range(n-1))

array = [1, 3, 5, 2, 4]
swap_pairs = [(1, 3), (2, 4)]
print(can_sort_with_swaps(array, swap_pairs))
```

Output: `True`

In this method, a graph is created from the swaps and DFS is used to find connected components, which are then sorted individually. By doing so, it ensures that elements can be placed in the correct order respective to the overall array’s order.

## Summary/Discussion

• Method 1: Union-Find. This method is very efficient for disjoint set operations. It excels in scenarios with multiple union and find queries. Its downside is the additional complexity involved in understanding and implementing the algorithm.
• Method 2: Graph Connectivity. This approach is more intuitive, especially for those familiar with graph theory. It provides a clear visual representation of the problem. However, it can be less efficient than Union-Find if there are a large number of swaps leading to a dense graph.