2、数据结构和设计模式(数据结构基础、字符串、设计模式与软件测试)

1、数据结构

排序[1]

A1插入排序:插入排序

直接插入排序是稳定的排序方法。基本思想:假设待排序的记录存放在数组R[1...n]中,初始时,R[1]自成一个有序区,无需区为R[2...n],依次将R[i]插入到当前有序区R[1...i-1]中,生成含n个记录的有序区。

void insert_sort(int a[], int n)
{
	int i,j,temp;
	for (i = 1; i < n; i++)
	{
		temp = a[i];
		for (j = i - 1; i >= 0 && temp < a[j]; j--)
		{
			a[j + 1] = a[j];
		}
		a[j+1] = temp;
	}
}

A2插入排序:shell排序

希尔排序算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d对每组中全部元素进行排序,然后用一个较小的增量对其进行再次分组,并对每个新组重新进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成 。希尔排序实质上是一种分组插入方法。

由于分组的存在,相等元素可能会分在不同组,导致它们的次序可能发生变化,因此希尔排序是不稳定的。

void shell_sort(int a[], int len)
{
	int h,i,j,temp;
	for (h = len / 2; h>0; h /=2)
	{
		for (i = h; i < len;i++)
		{
			temp = a[i];
			for (j = i - h; j >= 0 && temp < a[j];j-=h)
			{
				a[j + h] = a[j];
			}
			a[j + h] = temp;
		}
	}
}

B1交叉排序:冒泡排序

冒泡排序是稳定的排序。方法为:将被排序的记录数组A[1...n]垂直排列,每个记录A[i]看作重量为A[i]气泡。从下往上扫描数组A,凡扫描到违反原则(请气泡在上)的轻气泡,使其上浮。具体步骤:
  1. 初始状态下,A为无序区;
  2. 第一趟扫描:对每对气泡A[j+1]<A[j],交换内容;
  3. 第二趟扫描:扫描A[2...n];
  4. 第i趟扫描:A[1...i-1]和A[i...n]分别为当前有序区和无序区,扫描无序区;
  5. 经过n-1趟扫描得到有序区A[1...n];
void bubble_sort(int a[], int len)
{
	int temp;
	for (int i = 0; i < len - 1; i++)
	{
		for (int j = len - 1; j > i; j--)
		{
			if (a[j - 1] > a[j])
			{
				temp = a[j];
				a[j] = a[j - 1];
				a[j - 1] = temp;
			}
		}
	}
}
存在这样的问题,假设进行第i次扫描前,数组已经排好序,还是会进行下一次扫描,显然以后的扫描没有必要。改进程序如下:
void bubble_sort2(int a[], int len)
{
	int temp,exchange;
	for (int i = 0; i < len - 1; i++)
	{
		exchange = 0;
		for (int j = len - 1; j > i; j--)
		{
			if (a[j - 1] > a[j])
			{
				temp = a[j];
				a[j] = a[j - 1];
				a[j - 1] = temp;
				exchange = 1;
			}
		}
		if (exchange != 1)
			return;
	}
}

B2交叉排序:快速排序

采用一种分治的策略,通常称其为分治法(Divid-and-Conquer Method)。基本思想是:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。设待排序的无序区为A[low...high],具体步骤:

  1. 分解:在A中任选一个记录作为基准pivot,以此为基准将当前无序区划分为左、右两个子区间A[low...pivot-1]和A[pivot+1...high],并使左子区间中所有记录的关键字均小于等于基准,右子区间均大于等于基准;
  2. 求解:通过递归调用快速排序对左、右子区间快速排序;
  3. 组合:递归调用结束时,左、右子区间已有序;

void quick_sort(int a[], int low, int high)
{
	if (low < high)
	{
		int i = low;
		int	j = high;
		int pivot = a[low];
		while (i < j)
		{
			while (i < j&&a[j] >= pivot)
				j--;
			if (i < j)
				a[i++] = a[j];

			while (i < j&&a[i] <= pivot)
				i++;
			if (i < j)
				a[j--] = a[i];
		}
		a[i] = pivot;		  //pivot移到最终位置
		quick_sort(a,low,i-1);
		quick_sort(a,i+1,high);
	}
}


void print_array(int a[],int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;
}

C1选择排序:直接选择排序

选择排序是不稳定的。直接选择排序基本思想:n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。

  1. 初始状态:无序区A[1...n],有序区为空;
  2. 第1趟排序:在无序区A中选出最小的记录A[k],将其与无序区的第1个记录A[1]交换,使A[1...1]和A[2...n]分别为记录个数增加1的有序区和记录个数减少1的新无序区;
  3. 第i趟排序:第i趟排序开始时,当前有序区和无序区分别为A[1...i-1]和A[i...n]。该趟排序从当前无序区中选出关键字最小记录A[k],将其与无序区第1个记录A[i]交换,使A[1...i]和A[i+1...n]分别变为记录个数增加1的新有序区和记录个数减少1的新无序区;
void simple_selection_sort(int a[], int len)
{
	int i, j, nSmall, nIndex;
	for (i = 0; i < len; i++)
	{
		//从a[0]开始
		nSmall = a[i];
		nIndex = i;
		//找出i到len-1的最小值
		for (j = i; j < len; j++)
		{
			if (a[j] < nSmall)
			{
				nSmall = a[j];
				nIndex = j;
			}
		}
		//最小值和初始值进行交换
		a[nIndex] = a[i];
		a[i] = nSmall;
	}
}

堆排序

堆排序定义:n个序列A1...An称为堆,有下面两种不同类型的堆。
小根堆:所有子节点都大于其父节点;
大根堆:所有子节点都小于其父节点;
对本质上满足如下性质的完全二叉树:树种任意非叶节点的关键字均大于(或不小于)其左、右子节点的关键字。用大根堆排序的基本思想:
  1. 将初始A[1...n]建成一个大根堆,此堆为初始的无序区;
  2. 将关键字最大的记录A[1](堆顶)和无序区的最后一个记录A[n]交换,由此得到新的无序区A[1...n-1]和有序区A[n],且满足A[1...n-1]<=A[n];

编程实现单链表的逆置

node *reverse(node *head)
{
	node *p1, *p2, *p3;

	if (head == NULL || head->next == NULL)
		return head;

	p1 = head, p2 = p1->next;
	while (p2)
	{
		p3 = p2->next;
		p2->next = p1;
		p1 = p2;
		p2 = p3;
	}

	head->next = NULL;
	head = p1;
	return head;
}

2、字符串

gets()、scanf()、puts()、printf()

gets()仅在回车符时停止接受字符,一次只能输入一个字符串;
scanf()停止字符包括空格、tab、等,一次可输入多个字符串;
puts()输出且只能输出字符串,一次输出一个,输出完成后自动添加换行符;使用时需注意,在输出字符串时要遇到结束符“\0”才停止,如果程序末尾不添加字符串结束符,输出正常字符串后边会出现任意字符。
printf()

字符串的转化(itoa、atoi)

int Str2Int(const char* str)
{
	if (str == NULL)
		return 0;
	int num = 0;
	while (str!=0)
	{
		num =num*10 + (*str - '0');
		str++;
	}
	return num;
}

char Int2Str(int num)
{
	char temp[1024], str[1024];
	int i = 0, j = 0;

	while (num)
	{
		temp[i] = num % 10 + '0';
		i++;
		num /= 10;
	}

	temp[i] = 0;
	i -= 1;

	while (i >= 0)
	{
		str[j] = temp[i];
		j++;
		i--;
	}

	str[j] = 0;
	return str[j];
}
//考虑测试用例
//功能测试:输入正数、负数和0
//边界值测试:最大正整数、最小正整数
//特殊输入测试:输入字符串为空指针、空字符、非数字字符等
enum status{kValid=0,kInvalid};
int g_nStatus = kValid;

int Str2Int(const char* str)
{
	g_nStatus = kValid;
	long long num = 0;

	if(str != NULL&&*str!='\0')
	{
		bool minus = false;
		//判定=-
		if (*str == '+')
			str++;
		else if (*str == '-')
		{
			str++;
			minus = true;
		}
		
		//
		if (*str != '\0')
		{
			num = Str2Int(str,minus);
		}
	}

	return num;
}

long long Str2Int(const char* temp, bool minus)
{
	long long num = 0;

	while (*temp != '\0')
	{
		if (*temp >= '0'&&*temp <= '9')
		{
			int flag = minus ? -1 : 1;
			num = num * 10 + (*temp - '0');

			if ((!minus&&num > 0x7FFFFFFF) || (minus&&num < (signed int)0x80000000))
			{
				num = 0;
				break;
			}

			temp++;
		}
		else
		{
			num = 0;
			break;
		}
	}

	if (*temp == '\0')
	{
		g_nStatus = kValid;
	}

	return num;
}

字符串拷贝&&char* strcpy(char* dst,const char* src)为什么还要char*类型的返回值?

//字符串拷贝实现
char* strcpy(char* strDst, const char* strSrc)
{
	assert((strDst!=NULL)&&(strSrc!=NULL));
	char* temp = strDst;
	while ((*strDst++ = *strSrc++) != '\0')
		NULL;

	return temp;
}
//微软实现
char* cdecl strcpy(char* dst, const char* src)
{
	char* cp = dst;
	while (*cp++ = *src++);

	return dst;
}

char*类型返回值是为了实现链式表达式,返回具体值。

例如:int length=strlen(strcpy(srcDsr,"hello world"));

编写函数实现把一个char型字符串循环右移n个

//Solution1
void LoopMove(char* str, int steps)
{
	int n = strlen(str)-steps;
	char temp[9999];
	strcpy(temp,str+n);
	strcpy(temp+steps,str);
	*(temp + strlen(str)) = '\0';
	strcpy(str,temp);
}
//Solution2
void LoopMove(char* str, int steps)
{
	int n = strlen(str) - steps;
	char temp[9999];
	memcpy(temp,str+n,steps);
	memcpy(str+steps,str,n);
	memcpy(str,temp,steps);
}

字符串中连续出现次数最多的子串

//字符串中连续出现次数最多的子串
pair<int, string> MaxNumofSubtrings(const string &str)
{
	vector<string> substrs;
	int nMaxNum = 1, Num = 1;
	string substring;
	int i, len = str.length();
	for (i = 0; i < len; i++)
		substrs.push_back(str.substr(i,len-i));

	for (i = 0; i < len; ++i)
	{
		for (int j = i + 1; j < len; ++j)
		{
			Num = 1;
			if (substrs[i].substr(0, j - i) == substrs[j].substr(0, j - i))
				++Num;

			for (int k = j + (j - i); k < len; k += j - i)
			{
				if (substrs[i].substr(0, j - i) == substrs[k].substr(0, j - i))
					++Num;
				else
					break;
			}

			if (Num>nMaxNum)
			{
				nMaxNum = Num;
				substring = substrs[i].substr(0,j-i);
			}
		}
	}

	return make_pair(nMaxNum, substring);
}

输入一字符串,找出其中出现的相同且长度最长的字符串,输出它及其首字符位置

string str,temp;
	cout << "请输入字符串:" << endl;
	cin >> str;

	for (int i = str.length() - 1; i > 1; i--)
	{
		for (int j = 0; j < str.length(); j++)
		{
			if (j + i < str.length())
			{
				size_t t = 0, num = 0;
				temp = str.substr(j,i);

				t = str.find(temp);
				num = str.rfind(temp);
				if (t != num)
				{
					cout << &temp << " " << t + 1 << endl;
					return 0;
				}
			}
		}
	}




3、设计模式与软件测试

测试


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值