编程珠玑 -- swap section

编程珠玑第二篇主要提出了三个思想:

1。 二分查找。(binary search)

2。 交换分区。(swap section)

3。 签名。        (signature)

 

二分查找比较耳熟能详,签名的话我觉得是个很好的思想,差不多相当于hashMap吧。这一节我主要关注的是交换分区的算法。

 

问题描述大概如下:旋转一个长度为n的字符串i个位置。比如,字符串"abcdefgh", n=8, i=3,旋转过后得到"defghabc"。

最容易想到的当然是把前i个字符保存起来,然后把后面的n-i个字符移到前面去,然后再把保存起来的i个字符串粘到n-i个字符后面。时间复杂度为O(n),空间复杂度为O(i),我们需要多余的i个空间来存放那些被剪切的字符。有什么办法可以使空间复杂度降为O(1)呢?

 

书里面提到了Gries and Mills的一篇总结报告,<swap section>。我去网上找了来。报告中提到了三种算法:

1. Dolphine swap.

    Dolphine swap的基本思路是,把x[0]先保存起来,然后把x[i]放到x[0],把x[2i]放到x[i]...如果i和n互质的话,一个循环就能将所有的字符串都放好,最后把t填充到最后一个空位中。如果i和n不互质的话,则需要做gcd个循环。

void dolphine( char* s, int pos ){
	int n=strlen(s);
	int r = gcd( n, pos );
	int i=0;
	for( i=0; i<r; i++ ){
		char t=s[i];
		int j=i;
		while(1){
			int k = j+pos;
			if( k >= n )
				k -= n;
			if( k == i )
				break;
			s[j] = s[k];
			j =k ;
		}
		s[j] = t;
	}
}

//顺便给出gcd的算法
int gcd( int a, int b ){
	while( a != b ){
		if( a>b )
			a -= b;
		else
			b -= a;
	}
	return a;
}

 

2. Successive swap

     假设i<n-i,最容易想到的是,前i个字符串最终一定要放在最后面i个位置。把这前后两个i位的字符串进行交换,则接下来的问题变成对前面长度为n-i的字符串进行同样的操作。如果i>n-i,则交换后变成对后面n-i个字符串进行操作。

void successiveSwap( char*s, int pos ){
	if( pos == 0 || pos == strlen(s) )
		return;
	int i, p;
	i = p = pos;
	j = strlen(s)-i;
	while( i!=j ){
		if( i<j ){
	//swap( char*s, int a, int b, int m)代表将字符串s[a...a+m-1]与s[b...b+m-1]进行交换
			swap( s, p-i, p+j-i, i );
			j -= i;
		}else{
			swap( s, p-i, p, j );
			i -= j;
		}
	}
	swap( p-i, p, i );
}  

3. Reversal swap

Reversal swap是最简单的。把设a=s[0...i-1], b=s[i...n-1],设如下:设a="abcd", b="kgthe",

a = reverse(a) = "dcba",

b = reverse(b) = "ehtgk"

reserse(ab) = "kgheabcd", 不正是我们想要的结果了么?

 

void reversalSwap( char* s, int pos ){
	reverse( s, 0, pos-1 );
	reverse( s, pos, strlen(s)-1 );
	reverse( s, 0, strlen(s)-1 );
	}

 

Bentley在后面的习题中提出了对这三种算法进行比较的结果,考虑比较大的数组,这样的话,该数组将占用超过一页的内存,随着旋转距离的增加,dolphine算法导致页面的不断换入换出,因此算法的时间开销变大;而SuccessiveSwap由于有比较良好的locality,时间开销是三种算法中最稳定的。ReversalSwap也比较稳定,但是因为进行了两次交换,所以时间在SuccessiveSwap之上。

附件是Gries and Mills的<swap section>

 

这段日子重新开始看《编程珠玑》以及这篇总结,发现之前给出的代码有点问题,修改了一下。而且我觉得第二章提出的另外两个问题也很有趣,值得我记录下来。

 

所谓的二分查找并不是我们通常以为的那种给定一个排好序的数组,然后进行查找,虽然思想是一致的。比如说给出99个0到100的数,让你找出在该范围中不在这组数据中的一个数(只有99个数,如果没有重复,至少也有两个数不在该数组中)。使用二分查找的思想,对数据进行顺序处理,比方说大于50的数放到一边,小于的放到另一边;至少会有一边的数据小于50个,那么肯定可以在这边的数中找到不存在的那个数,依次类推下去。

 

找同位词,设计一种计算方法,可以使得同位词拥有相同的计算结果。书中给出的想法是对词的字母进行排序,得到一个对应的结果。然后将所有单词的计算结果进行排序,从而使得同位词将被排在一起。

你可能感兴趣的:(编程,算法)