关于手写代码

57 篇文章 1 订阅
40 篇文章 1 订阅

        在面试的过程中,软件主管为了考察面试者的代码水平,往往让面试者手写一段经典代码,比如字符串拷贝函数strcpy()、字符串连接函数strcat()、单链表反转、冒泡排序、二叉树的三种遍历(先序、中序、后序)等。这就需要面试者在复习的过程中理清算法的原理、画算法流程图或者UML类图、会写伪代码哪怕是中文也行。
        下面介绍常见的几种手写代码:

  1. 字符串拷贝函数strcpy()
//字符串复制,从src拷贝到dst
char* strcpy(char *dst, const char* src) {
	assert(dst != NULL && src != NULL );

	char *ret = dst;
	while ((*dst++ = *src++) != '\0');
	return ret;
}
  1. 字符串连接函数strcat()
char *strcat(char* strDst, const char* strSrc) {
	assert(strDst != NULL && strSrc != NULL);
	char *ret = strDst;
	while (*strDst != '\0'){
		strDst++;
	}
	while ((*strDst++ = *strSrc++) != '\0');

	return ret;
}
  1. 单链表反转
struct ListNode {
	int         m_data;
	ListNode   *m_next;
	ListNode(int x) : m_data(x), m_next(NULL) {}
};

ListNode* reverseList(ListNode *head) {
    ListNode *pre = NULL, *next = NULL;
	ListNode *cur = head;
	while (cur != NULL) {
		next = cur->m_next;
		cur->m_next = pre;
		pre = cur;
		cur = next;
	}
	return pre;
}

//或者如下:
ListNode* reverseList(ListNode *head) {
    ListNode *p = NULL, *q = NULL;
	ListNode *cur = head;
	while (cur != NULL) {
		q = cur->m_next;
		cur->m_next = p;
		p = cur;
		cur = q;
	}
	return p;
}
  1. 冒泡排序
#include <stdlib.h>
#include <iostream>
using namespace std;
template<typename T>
//整数或浮点数皆可使用
void bubble_sort(T arr[], int len)
{
	int i, j; T temp;
	for (i = 0; i < len - 1; i++)
		for (j = 0; j < len - 1 - i; j++)
			if (arr[j] > arr[j + 1]) {
				temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
}
int _tmain(int argc, _TCHAR* argv[])
{
	int arr[] = { 61, 17, 29, 22, 34, 60, 72, 21, 50, 1, 62 };
	int len = (int) sizeof(arr) / sizeof(*arr);
	bubble_sort(arr, len);
	for (int i = 0; i < len; i++)
		cout << arr[i] << ' ';
	cout << endl;
	float arrf[] = { 17.5, 19.1, 0.6, 1.9, 10.5, 12.4, 3.8, 19.7, 1.5, 25.4, 28.6, 4.4, 23.8, 5.4 };
	len = (int) sizeof(arrf) / sizeof(*arrf);
	bubble_sort(arrf, len);
	for (int i = 0; i < len; i++)
		cout << arrf[i] << ' ';

	cout << endl;
	system("pause");
	return 0;
}
  1. 二叉树的先序、中序、后序遍历,递归方式实现
typedef char ElemType;
typedef struct btnode
{
	ElemType data;
	struct btnode* lchild;
	struct btnode* rchild;
}BTNode;

//先序遍历(根-->左子树 -->右子树)
void PreOrder(BTNode *b)
{
	if (b != NULL)
	{
		printf("%c ", b->data); //访问根结点
		PreOrder(b->lchild);
		PreOrder(b->rchild);
	}
}

//中序遍历(左子树 --> 根 -->右子树)
void InOrder(BTNode *b)
{
	if (b != NULL)
	{
		InOrder(b->lchild);
		printf("%c ", b->data); //访问根结点
		InOrder(b->rchild);
	}
}

//后序遍历(左子树 -->右子树 --> 根)
void PostOrder(BTNode *b)
{
	if (b != NULL)
	{
		InOrder(b->lchild);
		InOrder(b->rchild);
		printf("%c ", b->data); //访问根结点
	}
}

  1. 二叉树的先序、中序、后序遍历,非递归方式实现
//-------------- 非递归实现
//先序遍历的非递归方法:由先序遍历的过程可知,先访问根结点,再访问左子树,最后访问右子树。
//因此,先将根结点进栈,在栈不空时循环: 出栈p,访问*p结点,将其右孩子进栈,再将左孩子结点进栈。
//算法如下:
void PreOrder1(BTNode *b)
{
	BTNode *St[100], *p;
	int top = -1;
	if (b != NULL)
	{
		top++;        //根结点入栈
		St[top] = b; 
		while (top > -1)      //栈不为空时循环
		{
			p = St[top];      //进栈并访问该结点
			top--;
			printf("%c ", p->data);

			if (p->rchild != NULL)   //右孩子结点入栈
			{
				top++;
				St[top] = p->rchild;
			}

			if (p->lchild != NULL)   //左孩子结点入栈
			{
				top++;
				St[top] = p->lchild;
			}
		}
		printf("\n");

	}
}

//中序遍历的非递归方法:由中序遍历的过程可知,采用一个栈保存需要返回的结点指针,先扫描(并非访问)根结点的
//所有左结点并将它们一一入栈。然后,出栈一个结点,显然该结点没有左孩子结点或者右孩子结点已访问过,则访问它。
//接着,扫描该结点的右孩子结点,将其进栈,再扫描该右孩子结点的所有左结点并一一入栈,如此这样,直到栈空为止。
//算法如下:
void InOrder1(BTNode *b)
{
	BTNode *St[100], *p;
	int top = -1;
	if (b != NULL)
	{
		p = b;
		while (top > -1 || p != NULL)
		{
			while (p != NULL)     //扫描*p的所有左结点并入栈
			{
				top++;
				St[top] = p;
				p = p->lchild;
			}

			if (top > -1)
			{
				p = St[top];            //出栈*p的结点
				top--;
				printf("%c ", p->data); //访问之
				p = p->rchild;          //扫描*p的右孩子结点
			}
		}
		printf("\n");
	}
}

//后序遍历的非递归方法:由后序遍历的过程可知,采用一个栈保存需要返回的结点指针,先扫描根结点的
//所有左结点并将它们一一入栈,出栈一个结点*b即当前结点,然后扫描该结点的右孩子结点并入栈,再扫描
//该右孩子结点的所有左结点并入栈。当一个结点的左、右孩子结点均被访问后再访问该结点,如此这样,
//直到栈空为止。
//    其中的难点是如何判断一个结点*b的右孩子已访问过,为此需要用p保存刚刚访问过的结点(初值为NULL),
//若b->rchild = p成立(在后序遍历中,*b的右孩子结点一定刚好在*b之前访问),说明*b的左、右子树均已访问,
//现在应访问*b.
//    从上面的过程可知,栈中保存的是当前结点*b的所有祖先结点(均未访问过)。
//算法如下:
void PostOrder1(BTNode *b)
{
	BTNode *St[100], *p;
	int flag, top = -1;               //栈指针置初值
	if (b != NULL)
	{
		do 
		{
			while (b != NULL)        //将*b的所有左结点进栈
			{
				top++;
				St[top] = b;
				b = b->lchild;
			}

			p = NULL;                  //p指向栈结点的前一个已访问的结点
			flag = 1;                  //设置b的访问标记为已访问过
			while (top != -1 && flag)
			{
				b = St[top];           //取出当前的栈顶元素
				if (b->rchild == p)    //右孩子不存在或后孩子已被访问,则访问*b
				{
					printf("%c ", b->data); //访问*b结点
					top--;
					p = b;                  //p指向被访问的结点
				}
				else
				{
					b = b->rchild;         //b指向右孩子结点
					flag = 0;              //设置未被访问的标记
				}
			}
		} while (top != -1);
		printf("\n");
	}
}
  1. 求二叉树的高度
//求二叉树的高度
int BTNodeDepth(BTNode *b)
{
	int lchildDep = 0, rchildDep = 0;
	if (b == NULL)
		return 0;               //空树的高度为0
	else{
		lchildDep = BTNodeDepth(b->lchild);  //左子树的高度
		rchildDep = BTNodeDepth(b->rchild);  //右子树的高度
		return (lchildDep > rchildDep) ? (lchildDep + 1) : (rchildDep + 1); //取最大值再加1即可
	}
}

        8.将字符串中的所有空格去掉,要求时间复杂度O(n),空间复杂度O(1),并统计空格的个数。
       思路:
       a) pSpace指向第一个空格字符;
       b) pChar指向pSpace后的第一个非空格字符;
       c) 交换pSpace和pChar的内容,且++pSpace,使其指向下一个空格字符;
       d) 重复步骤b)和c),直到pSpace或pChar指向’\0’;
       e) *pSpace = ‘\0’,结束算法;

int removeSpace(char *pStr) {
	char *pSpace = NULL, *pChar = NULL;

	if (pStr == NULL) {
		return 0;
	}

	//统计空格个数
	char *pCnt = pStr;
	int cnt = 0;
	while (*pCnt != '\0') {
		cnt++;
	}

	//找到第一个空格字符
	for (pSpace = pStr; *pSpace != ' '; ++pSpace);

	while (*pSpace != '\0') {
		//找到空格后面的第一个非空字符
		for (pChar = pSpace; *pChar == ' '; ++pChar);

		if (*pChar != '\0') { //没有字符串末尾
			//将该字符与空格进行交换
			*pSpace = *pChar;
			*pChar = ' ';
			++pSpace;
		}
		else {
			break;
		}
	}

	//交换完成后,之间将pSpace的位置设置为\0结尾即可
	*pSpace = '\0';
	return cnt;
}

        9.二叉树的层次遍历,使用队列实现。

struct TreeNode {
	int   val;
	TreeNode  *left;
	TreeNode  *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
public:
	vector<vector<int>> levelOrder(TreeNode *root) {
		vector<vector<int>> res;
		if (!root)
			return res;
		queue<TreeNode*> qu;
		qu.push(root);
		while (!qu.empty()) {
			vector<int> tmp;
			int len = qu.size();
			for (int i = 0; i < len; i++) {
				TreeNode *node = qu.front();
				qu.pop();
				tmp.push_back(node->val);
				if (node->left)
					qu.push(node->left);
				if (node->right)
					qu.push(node->right);
			}
			res.push_back(tmp);
		}
		return res;
	}

};
  1. 判断一个二叉树树是否为二叉搜索树。
    二叉搜索树的判断条件:若中序遍历该二叉树,得到的数组为从小到大的有序排列,则该树为二插搜索树,否则不是。
    可以用一个静态变量保持当前节点的前驱节点,若当前节点的值小于前驱节点,说明该树不是一颗二叉树搜索树。
    代码如下:
bool isBST(TreeNode *root) {
	static TreeNode* prev = NULL;
	if(root != NULL) {
		if(!isBST(root->left)) {
			return false;
		}
		
		if(prev != NULL && root->data < prev->data) {
			return false;
		}
		
		prev = root;
		if(!isBST(root->right)) {
			return false;
		}
	}
	
	return true;
}

  1. 翻转一颗二叉树
TreeNode* invertTree(TreeNode* root) {
	if (root == NULL) {
		return NULL;
	}

	invertTree(root->left);
	invertTree(root->right);
	swap(root->left, root->right);

	return root;
}

  1. 手写一个String字符串类,包括构造函数、拷贝构造、赋值构造、析构函数。
class String {
public:
    String(const char *cstr = 0); //const的正确使用,默认参数
    String(const String &str); //参数格式
    String& operator=(const String &str); //返回值,操作符函数写法,参数格式
    ~String();
    String(String &&str) noexcept ; //移动构造函数参数格式,不抛出异常,声明和定义都要写,写在初始化列表前面
    String& operator=(String &&str) noexcept ; //返回类型,赋值运算符函数写法,参数类型,不抛出异常
private:
    char *m_data; //底层数据存储形式,私有的
};
 
String::String(const char *cstr) { //无返回值,定义不写默认参数
    if (cstr) { //注意空字符串的情况
        m_data = new char[strlen(cstr) + 1]; //求c语言格式字符串长度用strlen,动态分配内存
        strcpy(m_data, cstr); //拷贝c语言字符串用strcpy,目标参数在前
    }
    else {
        m_data = new char[1];
        *m_data = '\0'; //字符串以'\0'字符结尾
    }
}
 
String::String(const String &str) {
    m_data = new char[strlen(str.m_data) + 1]; //动态分配内存,求c语言字符串长度用strlen,字符串以'\0'字符结尾
    strcpy(m_data, str.m_data); //c语言字符串拷贝用strcpy
}
 
String& String::operator=(const String &str) {
    if (this == &str) return *this; //避免自我赋值
 
    delete[] m_data; //先释放=左边的字符串的数据指针
    m_data = new char[strlen(str.m_data) + 1]; //动态分配内存,求c语言字符串长度用strlen,字符串以'\0'字符结尾
    strcpy(m_data, str.m_data); //c语言字符串拷贝
    return *this; //返回引用
}
 
String::~String() {
    delete[] m_data; //释放内存
    m_data = nullptr; //指针置空
}
 
String::String(String &&str) noexcept { //承诺不抛出异常
    m_data = str.m_data;
    str.m_data = nullptr; //原来的数据指针置空
}
 
String& String::operator=(String &&str) noexcept { //承诺不抛出异常
    if (this == &str) return *this; //避免自我赋值
 
    delete[] m_data; //释放内存,注意[]
    m_data = str.m_data; //直接拷贝指针,不需要重新分配内存
    str.m_data = nullptr; //原指针置空
    return *this; //返回引用
}
  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值