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,凡扫描到违反原则(请气泡在上)的轻气泡,使其上浮。具体步骤:
- 初始状态下,A为无序区;
- 第一趟扫描:对每对气泡A[j+1]<A[j],交换内容;
- 第二趟扫描:扫描A[2...n];
- 第i趟扫描:A[1...i-1]和A[i...n]分别为当前有序区和无序区,扫描无序区;
- 经过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],具体步骤:
- 分解:在A中任选一个记录作为基准pivot,以此为基准将当前无序区划分为左、右两个子区间A[low...pivot-1]和A[pivot+1...high],并使左子区间中所有记录的关键字均小于等于基准,右子区间均大于等于基准;
- 求解:通过递归调用快速排序对左、右子区间快速排序;
- 组合:递归调用结束时,左、右子区间已有序;
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趟直接选择排序得到有序结果。
- 初始状态:无序区A[1...n],有序区为空;
- 第1趟排序:在无序区A中选出最小的记录A[k],将其与无序区的第1个记录A[1]交换,使A[1...1]和A[2...n]分别为记录个数增加1的有序区和记录个数减少1的新无序区;
- 第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称为堆,有下面两种不同类型的堆。
小根堆:所有子节点都大于其父节点;
大根堆:所有子节点都小于其父节点;
对本质上满足如下性质的完全二叉树:树种任意非叶节点的关键字均大于(或不小于)其左、右子节点的关键字。用大根堆排序的基本思想:
- 将初始A[1...n]建成一个大根堆,此堆为初始的无序区;
- 将关键字最大的记录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、设计模式与软件测试
测试