#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mp make_pair
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
typedef complex<double> point;
#define xx real()
#define yy imag()

#define REP(i, a, b) for(int i = (a); i < (int)(b); i++)
#define REPN(i, a, b) for(int i = (a); i <= (int)(b); i++)
#define FA(it, x) for(__typeof((x).begin()) it = (x).begin(); it != (x).end(); it++)
#define SZ(x) (int)(x).size()
#define BE(x) (x).begin(), (x).end()
#define SORT(x) sort(BE(x))
#define _1 first
#define _2 second

#define x1 gray_cat_x1
#define y1 gray_cat_y1

template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }

#define file "I1"

const double EPS = 1e-9;
const double PI = acos(-1.);
const int INF = 1e9;
const ll MOD = 1e9 + 7;

// Graph solving 2-SAT problem
// Works in O(n + m)

// Use:
// 1) Fill gg.n as number of variables (not number of vertices!!!! 
//    Number of vertices will be gg.n * 2)
// 2) Call gg.add_clause(a, val_a, b, val_b) for each clause.
//    For example, for clause !v[i] | v[j] call gg.add_clause(i, 0, j, 1)
// 3) Call gg.build()

// If gg.IsSatisfiable is false there is no solution
// Otherwise answer will be in boolean array gg.result

// Note number of vertices will be twice more than number of variables
const int MAXN = 1 << 17;

struct graph{
	// Number of variables (number of vertices will be 2 * n)
	int n;
	
	// Graph
	vi g[MAXN];
	
	// Reverse graph
	vi gr[MAXN];
	
	// For dfs
	char used[MAXN];
	
	// For topological sorting
	vector<int> sorted;
	
	// Indices of condensation components for vertices
	int col[MAXN];
	
	// Current condensation component index
	int cur_col;
	
	// If has solution
	bool IsSatisfiable;
	
	bool result[MAXN];
	
	void clear(){
		for(int i = 0; i < 2 * n; i++){
			g[i].clear();
			gr[i].clear();
		}
		n = 0;
	}
	
	// Add edge (process clause (a | b) where val_a indicates whether we use a or !a)
	void add_clause(int a, int val_a, int b, int val_b){
		g[(a << 1) ^ val_a ^ 1].pb((b << 1) ^ val_b);
		gr[(b << 1) ^ val_b].pb((a << 1) ^ val_a ^ 1);
		
		g[(b << 1) ^ val_b ^ 1].pb((a << 1) ^ val_a);
		gr[(a << 1) ^ val_a].pb((b << 1) ^ val_b ^ 1);
	}
	
	// Dfs with top sorting
	void dfs(int s){
		used[s] = 1;
		for(int i = 0; i < (int)g[s].size(); i++){
			int to = g[s][i];
			if (!used[to]){
				dfs(to);
			}
		}
		sorted.push_back(s);
	}
	
	// DFS in reverse graph
	void dfs_r(int s){
		used[s] = 1;
		col[s] = cur_col;
		for(int i = 0; i < (int)gr[s].size(); i++){
			int to = gr[s][i];
			if (!used[to]){
				dfs_r(to);
			}
		}
	}
	
	// Building condensation
	void get_condensation(){
		// Init
		for(int i = 0; i < 2 * n; i++){
			used[i] = 0;
		}
		sorted.clear();
	
		// First DFS series
		for(int i = 0; i < 2 * n; i++){
			if (!used[i]){
				dfs(i);
			}
		}
		
		// Init
		for(int i = 0; i < 2 * n; i++){
			used[i] = 0;
		}
		cur_col = 0;
		
		// Second DFS series
		for(int i = 2 * n - 1; i >= 0; i--){
			int s = sorted[i];
			if (!used[s]){
				cur_col++;
				dfs_r(s);
			}
		}
	}
	
	void build(){
		// Get condensation
		get_condensation();
		
		// Find satisfability
		IsSatisfiable = true;
		for(int i = 0; i < 2 * n; i += 2){
			if (col[i] == col[i ^ 1]){
				IsSatisfiable = false;
				break;
			}
			if (col[i] > col[i ^ 1]){
				result[i / 2] = false;
			} else {
				result[i / 2] = true;
			}
		}	
	}
};

graph gg;

struct segment_tree {
	// Elements count
	int n;
	
	// Values
	int vals[MAXN];
	
	// Tree size 
	int sz;
	
	// Recursive build
	void build(int ind, int tl, int tr){
		if (tl == tr){
			vals[tl] = ind;
			sz = max(sz, ind);
			return;
		}
		int tm = (tl + tr) >> 1;
		build(ind << 1, tl, tm);
		build((ind << 1) + 1, tm + 1, tr);
	}		
	
	// Build (for call)
	void build(){
		build(1, 0, n - 1);
	}
	
	// Add edges to parents
	void add_to_par(int ind, int tl, int tr){
		if (tl == tr){
			return;
		}
		int tm = (tl + tr) >> 1;
		add_to_par(ind << 1, tl, tm);
		add_to_par((ind << 1) + 1, tm + 1, tr);
		gg.add_clause((ind << 1) - 1, 0, ind - 1, 1);
		gg.add_clause((ind << 1) + 1 - 1, 0, ind - 1, 1);
	}
	
	// Add edges to segment
	void add_to_seg(int from, int ind, int tl, int tr, int l, int r){
		if (l > r){
			return;
		}
		if (tl == l && tr == r){
			gg.add_clause(vals[from] - 1, 0, ind - 1, 0);
			return;
		}
		int tm = (tl + tr) >> 1;
		add_to_seg(from, ind << 1, tl, tm, l, min(r, tm));
		add_to_seg(from, (ind << 1) + 1, tm + 1, tr, max(l, tm + 1), r);
	}
};

segment_tree tt;

pii pp[MAXN];

int ind[MAXN][2], top_ind[MAXN];

void solve(){
	int n;
	scanf("%d", &n);
	REP(i, 0, 2 * n){
		scanf("%d", &pp[i]._1);
		pp[i]._2 = i / 2;
	}
	sort(pp, pp + 2 * n);
	REP(i, 0, 2 * n){
		ind[pp[i]._2][top_ind[pp[i]._2]++] = i;
	}
	
	int l = 0, r = 1e9;
	while(l + 1 < r){
		int mid = (l + r) / 2;
		
		gg.clear();
		tt.n = 2 * n;
		tt.build();
		gg.n = tt.sz;
		
		// x_i xor y_i
		REP(i, 0, n){
			gg.add_clause(tt.vals[ind[i][0]] - 1, 1, tt.vals[ind[i][1]] - 1, 1);
			gg.add_clause(tt.vals[ind[i][0]] - 1, 0, tt.vals[ind[i][1]] - 1, 0);
		}
		
		// parents in segment tree
		tt.add_to_par(1, 0, tt.n - 1);
		
		REP(i, 0, 2 * n){
			int l = lower_bound(pp, pp + 2 * n, mp(pp[i]._1 - mid + 1, -1)) - pp;
			int r = lower_bound(pp, pp + 2 * n, mp(pp[i]._1 + mid - 1, INF)) - pp - 1;
			tt.add_to_seg(i, 1, 0, tt.n - 1, l, i - 1);
			tt.add_to_seg(i, 1, 0, tt.n - 1, i + 1, r);
		}
		
		gg.build();
		
		if (gg.IsSatisfiable) {
			l = mid;
		} else {
			r = mid;
		}
	}
	
	printf("%d\n", l);
}   

int main(){

    //freopen(file".in", "r", stdin); freopen(file".out", "w", stdout);
    int t = 1;
    //cin >> t;
    while(t--){
        solve();    
    }
}


Measured memory_bytes: 38908