java c++数据结构面试题总结

一.判断链表是否存在环型链表问题:

  说明:判断一个链表是否存在环,例如下面这个链表就存在环,n1-->n2-->n3-->n4-->n5-->n2,环的开始结点是n5

  解法:这里有个比较简单的解法:设两个指针p1,p2,每次循环p1向前走一步,之向前走两步,直到p2碰到NULL指针(无环)或两个指针相等结束循环算法(有环),即如果两个指针相等则说明存在环。

代码如下:

         

/*节点数据结构*/
struct link{
	int data;
	link * next;
};

/*
*判断是否有环的方法
*
*参数来头节点的指针
*/
bool isLoop(link * head){
	//p1步长为1;p2步长为2
	link* p1 = head,*p2 = head;
	
	//如果只有一个节点或两个节点,直接return false
	if(head == NULL || head->next == NULL){
		return false;
	}
	
	//循环前进,直到p2走到NULL或p2追上p1
	do{
		p1 = p1->next;
		p2 = p2->next->next;
	}while(p2 && p2->next && p1!=p2);

	//如果有环
	if(p1 == p2){
		return true;
	else
		return false;
}


二.链表反转:

说明:链表反转,比如原链表是1-->2-->3-->4-->5 通过反转后成为5-->4-->3-->2-->1

解法一:利用三个指针,指向当前要反转的节点的指针,这个当前节点之前的节点指针,这个当前节点之后的节点指针;反转后再向后继续遍历

代码如下:

/*节点数据结构*/
struct linka{
	int data;
	link * next;
};

/*
*反转
*
*参数:头节点的指针的引用
*/
bool reverse(link * &head){
	//只有一个节点,即不用反转,直接返回
	if(head == NULL)
		return;

	//定义3个辅助指针,pre指向当前要反转的节点的前一个节点;cur为当前要反转的节点;ne指向当前反转的节点的下一个节点
	linka * pre,*cur,*ne;
	//初始化指针
	pre = head;
	cur = head->next;
	//循环,直到cur为NULL
	while(cur){
		ne = cur->next;
		cur->next = pre;
		pre = cur;
		cur = ne;
	}
	
	//反转到最后,设置头节点指针
	head->next = NULL;
	head = pre;
}
解法二:利用递归。这种方法的基本思想是在反转当前节点之前先调用递归函数反转后续节点。不过,这个方法有个缺点:在反转后的最后一个节点会形成一个环,所以必须将函数的返回节点的next设为NULL.因为要改变head指针,所以我用了引用

代码如下:

/*节点数据结构*/
struct linka{
	int data;
	link * next;
};

/*
*反转
*
*参数:头节点的指针的引用
*/
linka * reverse(linka * p,link * &head){
	if(p == NULL || p->next == NULL){
		head = p;
		return p;
	}else {
		linka* tmp = reverse(p->next,head);
		tmp->next = p;
		return p;	
	}
}

三.链表的合并:

说明:递增有序的2个单链表合并成一个递增有序的单链表,不用任何库函数调用

代码如下:

#include <iostream>
using namespace std;

/* 单链表节点 */
struct node{
	int value;
	node* next;
};

/* 给单链表添加节点 */
void insertNode(node* head, int value){
	node* p = head->next;
	if ( p == NULL ){
	   p = new node;
	   p->value = value;
	   p->next = NULL;
	   head->next = p;
	   return;
	}

	while ( p->next != NULL ){
	   p = p->next;
	}
	node* tmp = new node;
	tmp->value = value;
	tmp->next = NULL;
	p->next = tmp;
}

/* 遍历输出链表节点 */
void print(node* head){
	node* p = head->next;
	while ( p != NULL ){
	   cout << p->value << " ";
	   p = p->next;
	}
	cout << endl;
}


/* 下面实现不使用任何库函数, 利用交换的方法在原空间实现整体有序。 方法是先确定哪一个链表
的第一个节点的值小,把这个链表的头结点作为合并后链表的头结点,然后比较2个有序链表的当前节点
的值,如果代表最后合并链表的值小,则不用交换,否则把两个值交换,最后合并链表始终保持两个值中
的小值。另一个链表由于交换了一个元素,当前元素可能影响该链表的有序递增,对其进行调整使其保持
递增有序,然后重复上述动作,直到一个链表遍历结束,然后把剩余的链表连接起来就行。*/

/* 调整链表的第一个节点,使其变成递增有序 */
void chg2sort(node* head, node* &p){

	if (head->next == NULL ){ //没有节点,直接返回
		return;
	}

	node* s = head;
	while ( s->next != p ){ //s指向p的前一个节点
	   s = s->next;
	}

	//下面的一段找到第一个大于p节点值的节点
	node* q = p;
	node* r = q;
	
	while ( q != NULL ){
	
	   if ( q->value <= p->value ){
	    r = q; //r始终指向q的前一个节点
	    q = q->next;
	   }else {
	    break;
	   }
	}

	//下面调整指针,其实可以统一写出来,为了阅读清晰把q为NULL和非NULL分开写出来
	
	if ( q == NULL ){
	   r->next = p;
	   s->next = p->next;
	   p->next = NULL;
	}else if ( q != NULL ){
	   s->next = p->next;
	   r->next = p;
	   p->next = q;
	}
	
	//由于链表进行了调换,当前链表指针也需要改变
	p = s->next;
}


/* 两个有序链表进行合并 */
node* merge(node* head1, node* head2){
	node* head; //合并后的头指针
	node* p = head1->next;
	node* q = head2->next;

	//有一个链表为空的情况,直接返回另一个链表
	if ( p == NULL ){
	   head = head2;
	   return head;
	}else if ( q == NULL ){
	   head = head1;
	   return head;
	}

	//两个都不为空,先确定哪个链表作为合并后的链表
	if ( (p != NULL) && (q != NULL) ){
	   if ( p->value < q->value ){
	    head = head1;
	   }else{
	    head = head2;
	   }
	}

	node* p_prior; //始终指向p节点的前一个节点
	node* q_prior;

	while ( (p != NULL) && (q != NULL) ){
	   if ( p ->value < q->value ){
		    if ( head == head1 ){//如果链表1的值小于链表2的值,链表1的指针向下指
			     p_prior = p;
			     p = p->next;
		    }else if ( head == head2 ){
			     //进行当前节点值的交换
			     int tmp = p->value;
			     p->value = q->value;
			     q->value = tmp;
			     chg2sort(head1, p); //交换元素后的调整
			     q_prior = q;
			     q = q->next;
		    }
	   }else if ( p->value == q->value ){//链表1的值等于链表2时,两链表指针都向下指
		    p_prior = p;
		    p = p->next;
		    q_prior = q;
		    q = q->next;
	   }else if ( p->value > q->value ){
		    if ( head == head1 ){//如果链表1的值大于链表2的值,交接两节点的值后,排序链表2后,再下指
			     int tmp = p->value;
			     p->value = q->value;
			     q->value = tmp;
			     chg2sort(head2, q);
			     p_prior = p;
			     p = p->next;
		    }else if ( head == head2 ){
			     q_prior = q;
			     q = q->next;
	    	}
	   }
	}

	if ( p != NULL ){
	   q_prior->next = p;
	}

	if ( q != NULL ){
	   p_prior->next = q;
	}

	return head;
}

int main(){
	/* 建立有序链表A */
	int a[5] = {1, 5, 8, 10, 20};
	node* headA = new node;
	headA->next = NULL;
	
	for (int i = 0; i < 5; ++i){
	   insertNode(headA, a[i]);
	}
	
	print(headA);

	/* 建立有序链表B */
	int b[3] = {3, 4, 9};
	node* headB = new node;
	headB->next = NULL;
	
	for (int i = 0; i < 3; ++i){
	   insertNode(headB, b[i]);
	}
	
	print(headB);

	head = merge(headA, headB);
	print(head);
	
	return 0;
}


如果可以用库函数合并的话:代码如下:

Node* mergeAction(Node* head1, Node* head2){
//两个链表的合并操作 
        Node* head=(Node*)malloc(sizeof(Node)); 
        Node* q=head; //q指向合并后的链表的最后一个
        while(head1 && head2){ //
                if(head1->data<=head2->data){ 
                        Node* p=(Node*)malloc(sizeof(Node)); //p是新生节点的指针
                        p->data=head1->data; 
                        p->next=NULL; 
                        q->next=p; 
                        q=q->next; 
                        head1=head1->next; 
                } 
                else if(head1->data > head2->data){ 
                        Node* p=(Node*)malloc(sizeof(Node)); 
                        p->data=head2->data; 
                        p->next=NULL; 
                        q->next=p; 
                        q=q->next; 
                        head2=head2->next; 
                } 
        } 
        return head->next; 
}



四.判断两个数组中是否存在相同的数字,给定两个排好序的数据,怎么高效得判断这两个数组中存在相同的数字:

说明:O(n)算法,因为两个数组都是排序好的,所以只要一次遍历就行了,首先设两个下标,分别初始化为两个数组的起始地址,依次向前推进,推进的规则是比较两个数组中的数字,小的那个数组的下标各前推进一步,直到任何一个数组的下标到达数组末尾时,如果这时还没碰到相同的数字,说明数组中没有相同的数字。

代码如下:

bool findcommon2(int a[], int size1,int b[],int size2){
	int i = 0, j = 0;
	
	while(i<size1 && j<size2){
		if(a[i]==b[j])
			return true;
		
		if(a[i]>b[j])
			j++;//j标记b数组
		
		if(a[i]<b[j])
			i++;//i标记a数组
	}
	
	return false;
}

五.按单词反转字符串:

说明:单词用空格分开,如,Here is blog.csdn.net/wufenglong 经过反转后变为:blog.csdn.net/wufenglong is Here如果只是简单的将所有字符串翻转的话,可以遍历字符串,将第一个字符和最后一个交换,第二个和倒数第二个交换,依次循环。其实按照单词反转的话可以在第一遍遍历的基础上,再遍历一遍字符串,对每一个单词再反转一次,这样每个单词又恢复了原来的顺序

代码如下:

char * reverse_word(const char *str){
	int len = strlen(str);
	char * restr = new char[len+1];
	
	strcpy(restr,str);
	
	//首尾交换,i是首的索引 j是尾的索引
	for(int i=0,j=len-1;i<j;i++,j--){
		char tmp = restr[i];
		restr[i] = restr[j];
		restr[j] = tmp;
	}
	
	//再把每个单词反转
	int i,j,k = 0;
	while(k<len){
		i=j=k;
		while(restr[j]!=' ' && restr[j]!='\0')
			j++;//j为空格的索引
		
		k = j+1;//k为
		j--;
		
		//反转单词
		for(;i<j;i++,j--){
			char tmp = restr[i];
			restr[i] = restr[j];
			restr[j] = tmp;
		}
	}
	
	return restr;
}

六.字符串反转:

题意:给定一个字符串,一个这个字符串的子串,将第一个字符串反转,但保留子串的顺序不变。

例如:输入 每一个串 “this is wufl's Chinese site: http://blog.csdn.net/wufenglong”

子串:“wufl”

输出: gnolgnefuw/tne.ndsc/golb//:ptth:eits esenihC s'wufl si siht

说明:一般的方法是先扫描一边第一个字符串,然后用stack把它反转,同时记录下子串出现的位置。然后再扫描一遍把记录下来的子串再用stack反转,我用的方法是用一遍扫描数组的方法,扫描中如果发现子串,就将子串倒过来压入堆栈。

代码如下:

#include <stack>
using namespace std;

//reverse the string 's1' ,the substring 'token'
const char * reverse(const char * s1, const char * token){
	assert(s1 && token);
	
	stack<char> stack1;
	
	const char * ptoken = token, *head = s1, *rear =s1;
	
	while(*head !=''){
		while(*head !='' && *ptoken == *head){
			ptoken++;
			head++;
		}
		
		if(*ptoken==''){
			const char *p;
			
			for(p=head-1;p>=rear;p--){
				stark1.push(*p);
			}
			
			ptoken = token;
			rear = head;
		}else{
			stack1.push(*rear);
			head = ++rear;
			ptoken = token;
		}
	}
	
	char * returnV = new char[strlen(s1)+1];
	int i=0;
	
	while(!stack1.empty()){
		returnV[i++] = stack1.top();
		stack1.top();
	}
	
	returnV[i]="";
	return returnV;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值