《剑指offer》系列---1

最近一直在看剑指offer,这上面的题目都是比较考察编程能力的,打算做个记录,把写过的代码保存下来:

1.实现一个string类

面试官的考察点应该在以下几点:

1.模板类的书写

2.对于赋值函数考察的几点:(1)是否返回引用,因为只有返回引用,才能连续的进行赋值 (2)参数是否是常量 (3)是否是自身赋值(4)是否释放原来的内存 

一个完善的 string类如下:

#include <iostream>
#include <cstring>
using namespace std;

class String
{
public:
	String(const char *s = NULL);
	String(const String &rhs);
	String &operator=(const String &rhs);
	~String();
	friend ostream& operator<<(ostream &os, String&str);
private:
	char *m_data;
};

String::String(const char *str)
{
	if(str == NULL)
		m_data = NULL;
	else
	{
		int len = strlen(str);
		m_data = new char [len+1];
		strcpy(m_data, str);
	}
}
String::String(const String &rhs)
{
	if(rhs.m_data == NULL)
		m_data == NULL;
	else
	{
		int len = strlen(rhs.m_data);
		m_data = new char [len+1];
		strcpy(m_data, rhs.m_data);
	}
}

String::~String()
{
	delete[] m_data;
	m_data = NULL;
}

String& String::operator=(const String &rhs)
{
	/*if(&rhs == this)
		return *this;
	else
	{
		delete[] m_data;
		int len = strlen(rhs.m_data);
		m_data = new char [len+1];
		strcpy(m_data, rhs.m_data);
	}
	return *this;*/
	//摘自《剑指offer》
	if(&rhs != this)
	{
		String temp(rhs);
		char *t;
		t = m_data;
		m_data = temp.m_data;
		temp.m_data = t;
	}
	return *this;
}
ostream &operator<<(ostream &os, String &str)
{
	os<<str.m_data;
	return os;
}
int main()
{
	String obj1("12");
	String obj2(obj1);
	String obj3, obj4;
	obj4 = obj3 = obj2;
	cout<<obj1<<endl;
	cout<<obj2<<endl;
	cout<<obj3<<endl;
	cout<<obj4<<endl;
	return 0;
}

对于赋值操作符,如果我们在delete之后,new失败了怎么办,这不是异常安全的,所以<<剑指offer>>中给了我们一个方法

2.二维数组的查找:

有这样一个数组:

1      2     8     9

2      4     9     12

4     7     10     13

6     8     11     15

这个数组的特点是,每一行从左到右是递增的,每一列从上到下也是递增的,实现算法在其中查找某个数x

方法是这样:我们看最右上角的那个数,如果x大于9,说明第一行肯定小于x,我们可以把第一行删除,如果x小于9,我们可以把最后一列删除,这样查找打范围就缩小了

#include <stdio.h>

#define ROW 4
#define COLUMN 4

bool find_in_sort_array(int a[][COLUMN], int row, int column, int x)
{
	int m  = 0, n = column-1;

	if(row > 0 && column > 0 && a != NULL)
	{
		while(m < row && column >= 0 )
		{
			if(a[m][n] == x)
				return true;
			else if(a[m][n] > x)
				n--;
			else 
				m++;
		}
	}
	return false;
}

int main()
{
	int array[ROW][COLUMN] = {1,2,8,9,2,4,9,12,4,7,10,13,6,8,11,15};
	printf("please input a number to be finded\n");
	int c;
	scanf("%d",&c);
	if(find_in_sort_array(array, ROW, COLUMN, c))
		printf("finded\n");
	else	printf("not finded\n");
	return 0;
}

3.实现一个函数,把字符串中的每一个空格替换成“%20”,例如输入“we are happy”,输出“we%20are%20happy”

如果这个题目你是想着直接遍历这个字符串,然后每找到一个空格就把这个空格后面的字符后移一位的话,估计你是得不到面试官青睐的,因为移动多次是一个充分的过程,我们可不可以从后向前移动呢,这样我们就需要先遍历这个字符串,然后找到空格的个数,把指针指向空格个数+2+len的位置,这样再遍历这个字符串,从后往前把数据往后移动就可以了

代码如下:

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

//这是一个合并数组的题目,首先要考虑地一个数组是否足够大,如果不够就要重新申请内存空间。如果够的话我们从后往前合并两个数组到其中那个可以容纳所以数据的数组中
char *Replace_blank(char *s)
{
	if(s == NULL)//这是一个得分点
		return NULL;
	int len = 0, i =0 ;
	while(s[len] != '\0') 
	{
		if(s[len] == ' ')
			i++;
		len++;
	}

	char *p = s + (len+i*2);
	char *q = s + len;

	while(q >= s)//需要注意的细节,考虑了字符串刚开始的空格
	{
		if(*q == ' ')
		{
			strncpy(p-2,"%20",3);//需要注意的细节
			p-=3;
			q--;
		}
		else 
		{
			*p = *q;
			p--;
			q--;
		}
	}
	return s;
}

void main()
{
	char s[100];
	printf("pleas input a test string\n");
	fgets(s, 99, stdin);//fgets输入一个字符串
	printf("the input string is:\n%s\n",s);
	char *news;
	news = 	Replace_blank(s);
	printf("after Repleaced ,the string is:%s\n",news);
}

4.从尾到头打印单链表

这个题目让我想起了快速排序的递归和非递归实现,考察你对栈的本质的理解:

系统使用栈和我们自己使用栈

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

using namespace std;

typedef struct listNode
{
	int data;
	struct listNode*next;
}listNode;

void add_node(listNode **head, listNode *node)
{
	if((*head)->next == NULL)//测试过之后发现,写带头节点的单链表不容易出错
	{
		(*head)->next = node;
		return ;
	}
	else
	{
		listNode *p = *head;
		while(p->next != NULL)
			p = p->next;
		p->next = node;
	}
		
}

void delete_node(listNode *head, int val)
{
	listNode *p = head, *temp;
	while(p->next != NULL)
	{	
		temp = p->next;
		if(val == temp->data)
		{
			p->next = p->next->next;
			break;
		}
		p = p->next;
	}
}
void print_list(listNode *head)
{
	listNode *p = head->next;
	while(p != NULL)
	{
		printf("%d\n",p->data);
		p = p->next;
	}
}

//考点来了,在不允许修改单链表结构的情况下从尾到头打印单链表,怎么办呢?这不是一个递归的问题嘛
/*void print_from_tail(listNode *head)
{
	if(head == NULL)
		return;
	print_from_tail(head->next);
	printf("%d\n",head->data);
}*/

void print_from_tail(listNode *head)
{
	stack<listNode*> slist;
	listNode *p = head;
	while(p != NULL)
	{
		slist.push(p);
		p = p->next;
	}
	while(!slist.empty())
	{
		p = slist.top();
		printf("%d\n",p->data);
		slist.pop();
	}
}

int main()
{
	listNode *head = (listNode*)malloc(sizeof(listNode));
	if(head == NULL)
		exit(-1);

	int n = 5, i;
	for(i = 0; i < n; i++)
	{	
		listNode *node = (listNode*)malloc(sizeof(listNode));
		if(node == NULL)
			exit(-1);
		node->data = i;
		node->next = NULL;
		add_node(&head, node);
	}
	print_list(head);
	//delete_node(head, 4);
	//delete_node(head, 2);
	//print_list(head);
	print_from_tail(head->next);
	listNode *p = head->next, *temp;
	while(p != NULL)
	{
		temp = p;
		p = p->next;
		free(temp);
	}
	free(head);
	return 0;
}

5.重建一颗二叉树,例如,给定前序遍历{1,2,4,7,3,5,6,8}和中序遍历{4,7,2,1,5,3,8,6},构造这样的二叉树

其实这个题目如果理解了,二叉树选择题那简直太easy了,我们看前序遍历第一个数是1,这是一个根节点,然后中序遍历中1前面的

所有的数是左子节点,后面的所有的数是右子节点,然后在{4,7,2}这三个左子节点中,在前序遍历中地一个节点是2,这是个左子树的

根节点,然后对于中序遍历,{4,7}在2的左边,所以是2的左子节点,然后再看前序遍历,4是根节点,再看中序遍历,7是右子节点

就这样就构造出了这个二叉树:

#include <iostream>
using namespace std;

class BTreeNode
{
public:
	int data;
	BTreeNode *left, *right;
};

BTreeNode *construct_btree_func(int *pre_first, int *pre_last, int *in_first, int *in_last)
{
	int *first1 = pre_first, *last1 = pre_last;
	int *first2 = in_first, *last2 = in_last; 
	int len = 0;

	BTreeNode*root = new BTreeNode();
	root->data = *first1;

	if(first1 == last1 && first2 == last2 && *first1 == *first2)
		return root;//递归的出口

	while(*first1 != first2[len])
		len++;
	if(len > 0)
		root->left = construct_btree_func(first1+1, first1+len, 
				first2, first2+len-1);
	if(last1-first1 > len)
		root->right = construct_btree_func(first1+len+1, last1,
				first2+len+1, last2);
	return root;
}

BTreeNode *construct_btree(int pre[], int in[], int len)
{
	if(pre == NULL || in == NULL || len <= 0)
		return 0;
	else
		return construct_btree_func(pre, pre+len, in, in+len);
}
void post_visit(BTreeNode *root)
{
	if(root == NULL)
		return;
	else
	{
		post_visit(root->left);
		post_visit(root->right);
		cout<<root->data<<endl;
	}
}

int main()
{
	BTreeNode *root;
	int pre_arr[] = {1, 2, 4, 7, 3, 5, 6, 8};
	int in_arr[] = {4, 7, 2, 1, 5, 3, 8, 6};
	root = construct_btree(pre_arr, in_arr, sizeof(pre_arr)/sizeof(pre_arr[0])-1);
	post_visit(root);//后续遍历验证一下
	return 0;
}

6.用两个栈实现队列:

#include <iostream>
#include <stack>
#include <stdlib.h>
using namespace std;

template <typename T> class Queue
{
private:
	stack<T> stack1;
	stack<T> stack2;
public:
	Queue();
	~Queue();
	void append_node(const T& node);
	T delete_node();//此处不能返回引用,返回的是对局部变量的引用是错误的
};

template <typename T>//写错的地方没想到是这个格式
Queue<T>::Queue()
{
}

template <typename T>
Queue<T>::~Queue()
{
}

template <typename T> 
void Queue<T>::append_node(const T & node)
{
	stack1.push(node);
}
template <typename T>
T Queue<T>::delete_node()
{
	T temp;
	if(stack2.size() <= 0)//这样一个逻辑还是需要缜密的思维的,如果stack2不为空,则先把stack1的所有的数据导出来,然后一个负责输入数据的存储,每一次要输出了就把导入到另个正序的栈里面
	{
		while(!stack1.empty())
		{
			temp = stack1.top();
			stack1.pop();
			stack2.push(temp);
		}
	}
	if(stack2.size() == 0)
	{
		cout<<"queue is empty"<<endl;
		exit(-1);
	}

	temp = stack2.top();
	stack2.pop();
	return temp;

}

int main()
{
	Queue<int> queue;
	int i;
	for(i = 0; i < 5; i++)
		queue.append_node(i);
	for(i = 0; i < 6; i++)
		cout<<queue.delete_node()<<endl;
	queue.delete_node();

	return 0;
}

7.用两个队列实现栈:

#include <iostream>
#include <queue>
#include <stdlib.h>
using namespace std;

template<class T>
class Stack
{
public:
	Stack();
	~Stack();
	void insert(const T &x);
	T get();
private:
	queue<T> q1;
	queue<T> q2;
};
template<class T>
Stack<T>::Stack()
{}
template<class T>
Stack<T>::~Stack()
{}
template<class T>
void Stack<T>::insert(T const &x)
{
	if(q1.empty())
		q2.push(x);
	else
		q2.push(x);
}
template<class T>
T Stack<T>::get()
{
	T temp;
	if(q1.empty())
	{
		while(!q2.empty())
		{
			temp = q2.front();
			q2.pop();
			if(q2.empty())
				return temp;
			q1.push(temp);
		}
		cout<<"satck is empty"<<endl;
		exit(-1);
	}
	if(q2.empty())
	{
		while(!q1.empty())
		{
			temp = q1.front();
			q1.pop();
			if(q1.empty())
				return temp;
			q2.push(temp);
		}
		cout<<"satck is empty"<<endl;
		exit(-1);
	}
}
int main()
{
	Stack<int> stack;
	for(int i = 0; i < 5; i++)
		stack.insert(i);
	for(int i = 0;  i< 5; i++)
		cout<<stack.get()<<endl;
	stack.get();
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值