// 2sat.cpp
#include <vector>
#include <iostream>

#define rep(i,n) for(int i=0; i<(n); i++)

using namespace std;

typedef vector<int> vi;

// scc.cppのコピー
// 強連結成分分解 (Strongly Connected Component)
struct SCCGraph: public vector<vi> {

	vector<int> order;    // 属する強連結成分の番号(トポロジカル順序になっている)
	vector<int> vs;       // 帰りがけ順
	vector<bool> used;    // すでに調べたか

	using vector::vector;	// 継承コンストラクタ
	SCCGraph(const vector<vi> &v): vector::vector(v) {}	// コピーコンストラクタを使用可能にする

	// a -> b の辺を追加する。push_back()でも同じ
	void add_edge(int a, int b){
		(*this)[a].push_back(b);
	}

	// scc()で使う
	void dfs(int n, int k, vector<vi> &v, vector<vi> &rv){
		used[n] = true;
		order[n] = k;
		for(auto t: v[n]){
			rv[t].push_back(n);	// 逆辺のグラフを作成
			if( !used[t] ) dfs(t, k, v, rv);
		}
		vs.push_back(n);
	}

	// 強連結成分分解を行う
	int scc(){
		int N = size();
		used.assign(N, false);
		order.resize(N);
		vs.clear();
		vector<vi> rG(N), tmp(N);   // 辺を逆にしたグラフ用
		for(int n=0; n < N; n++){
			if( !used[n] ) dfs(n, n, (*this), rG);
		}
		used.assign(N, false);
		int k = 0;		// 強連結成分の番号
		for(int i = vs.size()-1; i >= 0; i--){
			if( !used[ vs[i] ] ) dfs(vs[i], k++, rG, tmp);
		}
		return k;
	}

	// 属する強連結成分が同じかどうか判定
	bool find(int x, int y){
		return order[x] == order[y];
	}

};
// 関数としてsccを使う。戻り値は、各ノードが所属する強連結成分の番号
vector<int> scc(const vector<vi> &v){
	SCCGraph g(v);
	g.scc();
	return g.order;
}


// 2-SAT
struct TwoSAT {
	SCCGraph graph;
	int N;
	vector<bool> val;	// 結果

	TwoSAT(int n): graph( (n+1)*2 ), val(n+1) {
		N = n;
	}

	void clear(){
		for(auto &&t: graph) t.clear();
	}

	// 辺( a -> b )を追加する。負の値は論理変数の否定(~a)を表す。
	// また、a の否定として a+N も使える。
	// a(>0) に対して、-a: 否定, a+N: 否定, -(a+N): 対偶=a
	void add_edge(int a, int b){
		if( a < 0 ) a = (3*N - a - 1) % (2*N) + 1;
		if( b < 0 ) b = (3*N - b - 1) % (2*N) + 1;
		graph.add_edge(a, b);
	}

	// 論理式( a V b ) を追加する。a,bは、論理変数の番号(0以外の整数)。否定は負の値で表す。
	void add_or(int a, int b){
		add_edge(-a, b);
		add_edge(-b, a);
	}

	// 判定を行う
	bool solve(){
		graph.scc();
		bool ret = true;
		for(int i=1; i <= N; i++){
			if( graph.order[i] == graph.order[N+i] ) ret = false;
			val[i] = (graph.order[i] > graph.order[N+i]);
		}
		return ret;
	}

	// 変数の値を取得する。
	bool value(int a){
		if( a < 0 ) return !val[-a];
		return val[a];
	}
};


// 各ノードの情報
template <typename T, T INIT>
struct node {
	T v, lazy;
	node(){
		v = INIT;
		lazy = 0;
	}
	// ノードの値の更新
	void merge(T vl, T vr){
		v = min(vl, vr);
	}
	// ノードの値の更新(lazy適用)
	T add(int l, int r, T _v=0){
		T sum = lazy + _v;
		v += sum;
		lazy = 0;
		return sum;
	}
	// 遅延分の変更(加算)
	void addLazy(T v){
		lazy += v;
	}
};

TwoSAT sat(90005);
const int SegOff = 20003;
void setRelation(int k, int v){
	k += SegOff;
	v += SegOff;
	//cout << "setRelation: " << k << " " << v << endl;
	sat.add_or( -k, -v );
}

const int INF = 1e9+7;

template <typename T, typename NODE=node<T,INF> >
class SegTree{
	// 遅延分の適応と伝播(現在のノードの値を確定させてLazyを伝搬させる)
	void propLazy(int a, int b, int k, T v=0){
		setRelation(k, N+v);
		//T lazy = data[k].add(a, b, v);
		//if( k < N ){
		//	data[k*2+0].addLazy(lazy);
		//	data[k*2+1].addLazy(lazy);
		//}
	}
	// セグメントツリーをたどる
	T prop(int l, int r, T v, int a, int b, int k=1){
		// 今いるノードの値を確定させる
		//propLazy(a, b, k);
		// たどる
		NODE nd;	// 結果保存用(範囲外の場合は初期値が返る)
		if( l <= a && b <= r ){
			// 完全に範囲内の場合
			propLazy(a, b, k, v);
			return data[k].v;
		}else if( l < b && a < r ){
			// 中途半端に含まれる場合
			int m = (a+b) / 2;
			T vl = prop(l,r,v, a, m, k*2);
			T vr = prop(l,r,v, m, b, k*2+1);
			data[k].merge( data[k*2].v, data[k*2+1].v );
			nd.merge( vl, vr );
		}
		return nd.v;
	}

public:
	vector<NODE> data;
	int N;
	SegTree(int _N){
		// _N <= (N = 2^k) を満たすNを見つける
		N = 1;
		while( N < _N ) N<<=1;
		data.resize(N*2);
	}
	void pRelation(){
		// 子がTrueなら、親もTrue
		rep(i,N) if(i){
			//cout << "pRelation: " << (SegOff + i*2) << " " << (SegOff + i) << endl;
			sat.add_edge(SegOff + i*2,   SegOff + i);
			sat.add_edge(SegOff + i*2+1, SegOff + i);
		}
	}
	void cRelation(int i, int v){
		int p = SegOff + N+i;
		//cout << "cRelation: " << p << " " << v << endl;
		sat.add_edge( p, v );
		sat.add_edge( v, p );
		sat.add_edge( -p, -v );
		sat.add_edge( -v, -p );
	}
	// セグメントツリーの更新(指定した場所の値をvに変更)
	void update(int l, T v){
		prop(l, l+1, v-get(l), 0, N);
	}
	// セグメントツリーの更新(範囲に加算)
	T add(int l, int r, T v){
		//cout << "sg.add(): " << l << " " << r << " :" << v << endl;
		return prop(l,r,v,0,N);
	}
	// 値を取得 [l,r)
	T get(int l, int r){
		return prop(l,r,0,0,N);
	}
	// 値を取得 [l,l+1)
	T get(int l){
		return prop(l,l+1,0,0,N);
	}
};


// Atcoder ARC069 Flags (http://arc069.contest.atcoder.jp/tasks/arc069_d)
#include <map>
#include <algorithm>
typedef pair<int,int> pii;
int main(){
	int N;
	int x[2][10005];
	vector<pii> mp;
	SegTree<int> sg(20005);

	cin >> N;
	rep(i,N){
		cin >> x[0][i] >> x[1][i];
		mp.emplace_back( x[0][i], i+1 );
		mp.emplace_back( x[1][i], -(i+1) );
	}
	sort( mp.begin(), mp.end() );

	auto fn = [&](int m){
		//cout << "fn " << m << endl;
		sat.clear();
		sg.pRelation();
		rep(i, mp.size()) sg.cRelation(i, mp[i].second);
		// range relation
		rep(i, mp.size()){
			// down
			auto it = lower_bound(mp.begin(), mp.end(), pii(mp[i].first - m + 1, -INF));
			int pos = (it - mp.begin());
			sg.add( pos, i, i );
			// up
			it = lower_bound(mp.begin(), mp.end(),  pii(mp[i].first + m, -INF));
			//it--;
			pos = (it - mp.begin());
			sg.add( i+1, pos, i );
		}
		//cout << "setup finish" << endl;
		bool ret = sat.solve();
		//cout << "ret: " << ret << endl;
		return ret;
	};

	int l=0, r=1e9;
	while( r-l > 1 ){
		int m = (l+r) / 2;
		if( fn(m) ){
			l = m;
		}else{
			r = m;
		}
	}

	cout << l << endl;

	return 0;
}

Measured memory_bytes: 52452