双指针的魅力

在解决问题过程中,经常碰到使用双指针解决的情况,使用双指针,可以很高效很快速的解决问题,罗列一下应用双指针的情况:

1,将给定的英文字符串进行反转,例如: I love programming。得到的结果是:.gnimmargorp evol I。下面给出核心代码:

#include<stdio.h>
#include<string.h>

void swap_string(char *p_start, char *p_end) {
	char temp;
	while(p_start <= p_end) {
		temp = *p_start;
		*p_start = *p_end;
		*p_end = temp;
		p_start++;
		p_end--;
	}
}

void main() {
	char str[] = "I love programming.";
	swap_string(str, str + strlen(str) - 1);
	printf("%s\n", str);
}

2,给定一个字符串,例如:"   test **   ",要求出去字母之外其他多余的字符,这也需要设定两个指针,一个慢指针和一个快指针,核心代码如下:

#include<stdio.h>
#include<string.h>

void trim_word(char *word) {
	char *p_slow = word;
	char *p_fast = word;
	while(*p_fast) {
		if(!(*p_fast >= 'a' && *p_fast <= 'z') && !(*p_fast >= 'A' && *p_fast <= 'Z')) {
			p_fast++;
		} else {
			*p_slow++ = *p_fast++;
		}
	}
	*p_slow = '\0';
}
void main() {
	char word[] = "   test **   ";
	trim_word(word);
	printf("%s\n", word);
}
如果给定的字符串是这样的:" test***ing ",这个算法也可以胜任的。

3,给定一个字符串,例如:"abccba",判断它是否是回文。可以应用数组解决,但是数组的索引速度没有指针的移动速度快,所以设定两个指针,一个指向字符串开始,一个指向字符串结尾,同步移动。核心代码如下:

#include<stdio.h>
#include<string.h>

int is_palindrome(char *str) {
	char *p_start = str;
	char *p_end = str + strlen(word) - 1;
	while(p_start < p_end) {
		if(*p_start != *p_end) {
			return 0;
		}
		p_start++;
		p_end--;
	}
	return 1;
}
void main() {
	char string[] = "abccba";
	if(is_palindrome(string)) {
		printf("string is palindrome.\n");
	} else {
		printf("string is not palindrome.\n");
	}
}

4,判断一个单链表是否有环,可以设置两个指针,一个指针每次移动一步,另一个指针每次移动两步,如果存在环的话,移动快的指针一定可以追上移动慢的指针。如下代码没有在真实的环境中跑过,只是一个基本的思路:

int is_cycling(LinkList L) {
	LinkNode *p_slow = L;
	LinkNode *p_fast = L;
	while(p_fast && p_fast->next) {
		p_fast = p_fast->next;
		p_fast = p_fast->next;
		p_slow = p_slow->next;
		if(p_slow == p_fast) {
			return 1;
		}
	}
	return 0;
}

5,给定一个链表,首先假设链表正向数或者反向数的第一个的标号都为1,不要受数组的影响。要求是找到链表中倒数第k个节点。链表的缺点之一就是不能像数组一样进行随机的存储和读取。比较笨拙的办法就是先遍历一遍链表,求出它的长度n,因为是找倒数第k个,所以我们只要找到整数第n-k个就可以了。另外一种比较巧妙的方法就是设定两个指针,一个指针先移动k-1步,那么它指向的就是链表中的第k个节点,然后再设置一个指针,指向链表头,假设头部中存储数据。那么两个指针同时移动,直至第一个指针指向空为止。核心代码如下,需要注意k的值超过链表长度的情况,同时,代码也只是基本思路,为经过验证:

LinkNode get_node(LinkList L, int k) {
	LinkNode *p_first = L;
	LinkNode *p_second = L;
	int i = 0;
	while(i < k) {
		p_first = p_first->next;
		i++;
	}

	if(p_first == NULL)
		return NULL;

	while(p_first != NULL) {
		p_first = p_first->next;
		p_second = p_second->next;
	}
	return p_second;
}

6,给定一个有序数组,然后给定一个数字m,在数组中找出两个数,使得这两个数的和与m相等。这个题目在双指针面前显得轻而易举:

#include<stdio.h>  
#include<stdlib.h> 

void FindTwoNumbers(int a[], int n, int dest)  {  
    int *e, *f;  
    e = a;  
    f = a + n - 1;  
    int sum;  
    sum = *e + *f;  
    while(sum != dest && e < f)  
    {  
        if(sum < dest) {  
            sum = sum - *e + *(++e); 
		} else {   
            sum = sum - *f + *(--f);
		}
    }  
    if(sum == dest) {  
		printf("%d=%d+%d\n",dest,*e,*f);
	} else {
		printf("not find\n");
	}
}  

void main()  
{   
    int num[] = {1, 2, 4, 7, 11, 15};  
    int temp = 9;  
    FindTwoNumbers(num, sizeof(num)/sizeof(int), temp);  
}  

7,就是字符串移位问题,这个也用到的双指针,假设给定字符串“abcdefg”,让字符串向做移动三位,得到的结果是:“defgabc”,这个怎么做呢,首先找到分界点,将字符串分为两部分,很自然的分界点应该和移动位数有关,故将字符串分为:abc,defg两部分,先分别将两部分翻转成:cbagfed,然后再将整个字符串翻转:defgabc。


8,新增加的这个在严格意义上不能算作双指针,但是也是类似于双指针,给出题目要求:一个数组,有正数和负数,怎么才能求出这个数组中最小绝对值。数组中有正数和负数,同样,设定两个标示,一个指向正数的positive,一个指向负数的negative,positive用来记录正数中最小的元素,negative用来记录负数中最大的元素,想一想,为什么要记录最大的负数,而不是最小的负数,画一个数轴就知道了。最后比较negative和positive的绝对值,返回最小的那个。

#include<iostream>
using namespace std;
#define MAX 1000
int get_min_abs_value(int *a, int n) {
	int min_positive = MAX;
	int max_negative = -MAX;
	int i;
	int min_abs_value;
	if(a == NULL || n == 0) {
		return -1;
	}
	for(i = 0; i < n; i++) {
		if(a[i] < 0) {
			if(a[i] > max_negative) {
				max_negative = a[i];
			}
		} else {
			if(a[i] < min_positive) {
				min_positive = a[i];
			}
		}
	}
	if(min_positive < (-1 * max_negative)) {
		min_abs_value = min_positive;
	} else {
		min_abs_value = -1 * max_negative;
	}
	return min_abs_value;
}

void main() {
	int a[] = {-9, -31, -4, 5, 8, 10, 32};
	int n = sizeof(a) / sizeof(int);
	cout << get_min_abs_value(a, n) << endl;
}

9,输入两个字符串,从第一字符串中删除第二个字符串中所有的字符。例如,输入”They are students.”和"aeiou”,则删除之后的第一个字符串变成”Thy r stdnts.”。这个题的思路其实也可以用双指针来完成。首先我们还得设定一个hash表,来记录在第二个字符串中有哪些字母出现。当hash值等于1的时候表示在第二个字符串,也就是要除去的字符串中出现,等于0的时候表示不出现。然后遍历第一个字符串,设定两个指针,一个p_slow, 一个p_fast,如果某个单词的hash值为1,说明要从原始字符串中除去这个单词,那么p_fast++。这个讲解起来有点不清晰,直接给出代码吧。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

void trim_string(char *string, char *trim_str) {
	int hash[256] = {0};
	int len_trim = strlen(trim_str);
	char *p_slow = string;
	char *p_fast = string;
	int i;
	for(i = 0; i < len_trim; i++) {
		hash[trim_str[i]]++;
	}
	while(*p_fast) {
		if(hash[*p_fast] > 0) {
			p_fast++;
		} else {
			*p_slow++ = *p_fast++;
		}
	}
	*p_slow = '\0';
}

void main() {
	char string[] = "They are students.";
	char trim[] = "aeiou";
	trim_string(string, trim);
	printf("%s\n", string);
}

10, remove duplicated characters in a string in O(n) times without using hash. And, I choose to use magic bit:

#include<stdio.h>
#include<string.h>

void remove_duplicate(char *str) {// assume all characters are little 
	char *p_slow = str;
	char *p_fast = str;
	int flag = 0;
	int value;
	int bits;
	while(*p_fast) {
		value = 1;
		bits = *p_fast - 'a';
		value = (value << bits);
		if((flag & value) == value) {
			p_fast++;
		} else {
			*p_slow++ = *p_fast++;
			flag |= value;
		}
	}
	*p_slow = '\0';
}

void main() {
	char str[] = "abcddbcdefghijm";
	remove_duplicate(str);
	printf("%s\n", str);
	getchar();
}


11, 反转句子中单词的顺序,即把一个英文句子的单词顺序倒转,忽略标点符号的处理,例如: I love linux programming. 操作之后结果: .programming linux love I。也需要设定双指针,这个情况和题目1有相似之处,但是又不完全的相同,需要在反转的过程中做一些判断,下面给出代码:

#include<stdio.h>
#include<string.h>
void swap_block(char *start, char *end) {
	char temp;
	while(start < end) {
		temp = *start;
		*start = *end;
		*end = temp;
		start++; 
		end--;
	}
}

void reverse_sentence(char *str) {
	char *p_slow = NULL;
	char *p_fast = NULL;
	swap_block(str, str + strlen(str) - 1);
	p_slow = str;
	p_fast = str;
	while(*p_fast) {
		if((*p_fast >= 'a' && *p_fast <= 'z') || (*p_fast >= 'A' && *p_fast <= 'Z')) {
			p_fast++;
		} else {
			swap_block(p_slow, p_fast - 1);
			while(!(*p_fast >= 'a' && *p_fast <= 'z') && !(*p_fast >= 'A' && *p_fast <= 'Z')) {
				p_fast++;//跳过多余的空格
			}
			p_slow = p_fast;
		}
	}
}

void main() {
	char str[] = "I love linux programming.";
	reverse_sentence(str);
	printf("%s\n", str);
	getchar();
}


12,给定一个链表,怎么在线性时间内判断是回文。http://blog.csdn.net/zzran/article/details/8510301

  • 16
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值