宝典——数据结构和设计模式

简介: 数据结构和设计模式数据结构1 约瑟夫环数组方式: 当i=1i=1时, f(m,k,i)=(m+k−1)f(m, k ,i) = (m+k-1) % m 当i!=1i!=1时,f(m,k,i)=(f(m−1,k,i−1)+k) f(m, k, i)= ( f(m-1, k, i-1)+k ) % mint fun(int m, int k, in

数据结构和设计模式

数据结构

1 约瑟夫环

数组方式:
i=1时, f(m,k,i)=(m+k1)
i!=1时,f(m,k,i)=(f(m1,k,i1)+k)

int fun(int m, int k, int i);

int main()
{
    for (int i = 1; i <= 10; i++)
    {
        cout<<fun(10, 3, i)<<" "; // 输出数值为[0,9]
        //cout<<fun(10, 3, i) + 1<<" "; // 输出数值为[1,10]
    }
}

int fun(int m, int k, int i)
{
    if (i == 1)
    {
        return (m + k - 1) % m;
    }
    else
    {
        return (fun(m - 1, k, i - 1) + k) % m;
    }
}

链表方式:

struct ListNode
{
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

class Josephus
{
public:
    Josephus(int x)
    {
        n = x;
        head = new ListNode(1);
        ListNode *pre = head;
        ListNode *cur;
        for (int i = 2; i <= x; i++)
        {
            cur = new ListNode(i);
            pre->next = cur;
            pre = cur;
        }
        cur->next = head;
    }

    void performKilling(int d)
    {
        d %= n;
        int count = 1;
        ListNode *pre;
        ListNode *cur = head;
        while (count++ <= n)  //while (cur->next != cur)
        {
            for (int i = 1; i < d; i++)
            {
                pre = cur;
                cur = cur->next;
            }
            cout<<cur->val<<" ";
            pre->next = cur->next;
            delete cur;
            cur = pre->next;
        }
        //cout<<cur->val;//delete cur;
    }

private:
    ListNode *head;
    int n; //size of the linked list
};

int main()
{
    Josephus *J = new Josephus(10);
    J->performKilling(3);
}

2 用两个栈实现一个队列的功能

假设两个栈A和B,且都为空。栈A提供push()功能,栈B提供pop()功能。

  • 入队列:入栈A。- 出队列:
  • 如果栈B不为空,直接弹出B的元素。
  • 如果栈B为空,则依次弹出栈A的元素并压入栈B中,再弹出B中的元素。

3 heap和stack的区别

经常需要操作的内存可分为以下几个类别:

  • 栈区:由编译器自动分配和释放,存放函数的参数值、局部变量的值等。
  • 堆区:一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。(它与数据结构中的堆是两回事,分配方式类似于链表)
  • 全局区(静态区):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统释放。
  • 文字常量区:常量字符串就是放在这里的。
  • 程序代码区:存放函数体的二进制代码。

heap是堆,stack是栈。
stack的空间由操作系统自动分配/释放,heap上的空间手动分配/释放。
stack的空间有限,heap有很大的自由存储区。
C中的malloc函数分配的内存空间即在堆上,C++对应的new操作符。
程序在编译期对变量和函数分配内存都是在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行。

stack的存取效率较快。
stack申请效率比较快,heap比较慢,而且容易产生碎片,不过用起来方便。

4 搜索引擎输入某个词时出来搜索建议,后台采用的数据结构是Trie树(字典树)

5 一个包含n个节点的四叉树,每一个节点都有4个指向孩子节点的指针,这个四叉树有_____个空指针。

4n(n1)=3n+1

6 相关概念

排序是否稳定:待排序文件中,具有相同关键字的记录,经过排序后记录之间的相对次序是否保持不变。
内部排序:整个文件都是放在内存中处理,排序时不涉及数据的内、外存交换。
外部排序:排序过程中要涉及内、外存交换。

分治法:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归的解决这些子问题,然后将这些子问题的解组合为原问题的解。

字符串

1 整数转化为字符串,并且不使用itoa

#include <iostream>
#include <cstring>
using namespace std;
void reverse(char *ch)
{
    char *p = ch;
    char *q = ch + strlen(ch) - 1;
    while (p < q)
    {
        char c = *p;
        *p++ = *q;
        *q-- = c;
    }
}
//没有考虑非法输入
void itoa(int n, char *ch)
{
    char *temp = ch;
    bool minus = false;
    if (n < 0)
    {
        n = -n;
        minus = true;
    }
    if (n == 0)
    {
        *ch++ = '0';
        *ch = '\0';
        return;
    }
    while(n)
    {
        *temp++ = n % 10 + '0';
        n /= 10;
    }
    if (minus)
    {
        *temp++ = '-';
    }
    *temp = '\0';
    reverse(ch);
}
int main()
{
    char ch[20] = {0};
    itoa(-12035, ch);
    cout<<ch<<endl;
}

2 字符串转化为整数

#include <iostream>
#include <cassert>
using namespace std;
//没有考虑非法输入
int atoi(const char *ch)
{
    assert(ch != NULL);
    int flag = 1;
    if (*ch == '-')
    {
        flag = -1;
        ch++;
    }
    else if (*ch == '+')
    {
        ch++;
    }
    int num = 0;
    while (*ch != '\0')
    {
        num = num * 10 + *ch++ - '0';
    }
    return num * flag;
}
int main()
{
    int n = atoi("-123450");
    cout<<n<<endl;
}

3 strcpy()

去掉不必要的安全检查,可以提高性能。
使用strncpy()需要手动在最后加上'\0'

#include <iostream>
#include <cassert>
using namespace std;
char* strcpy(char *strDest, const char *strSrc)
{
    assert((strDest != NULL) && (strSrc != NULL));
    char *temp = strDest;
    while ((*strDest++ = *strSrc++) != '\0');
    return temp;
}
char* strncpy(char *dest, const char *src, int n)
{
    assert(dest != NULL && src != NULL);
    char *temp = dest;
    while (n != 0 && (*dest++ = *src++) != '\0')
    {
        n--;
    }
    while (n != 0)
    {
        *dest++ = '\0';
        n--;
    }
    return temp;
}
int main()
{
    char ch1[20] = "123456789";
    char ch2[20];
    char *ch = strncpy(ch2, ch1, 30);
    cout<<ch<<endl;
}

4 程序输出结果

int main()
{
    char d[] = "123";
    char s[] = "123456789";
    strcpy(d, s);
    cout<<d<<endl;
    cout<<s<<endl;
}

输出为:123456789, 5789
如果交换d和s的定义顺序,则输出结果为123456789, 123456789

5 动态内存分配

指在程序的执行过程中动态的分配或者回收存储空间的内存分配方法。相对于静态内存分配的特点:
1. 不需要预先分配存储空间
2. 分配的空间可以根据程序的需要扩大和缩小
void *malloc(unsigned int size);
void free(void *p);

6 把一个字符串循环向右移n位

代码1:

void loopMove(char *str, int step)
{
    int len = strlen(str);
    step %= len;
    char *ch = new char[len + 1];
    strcpy(ch, str);
    for (int i = 0; i < len; i++)
    {
        str[i] = ch[(i - step + len) % len];
    }
}

代码2:

void loopMove(char *str, int step)
{
    int len = strlen(str);
    step %= len;
    char *ch = new char[len + 1];
    strcpy(ch, str + len - step);
    strcpy(ch + step, str);
    *(ch + strlen(str)) = '\0';
    strcpy(str, ch);
}

代码3:

void loopMove(char *str, int step)
{
    int len = strlen(str);
    step %= len;
    char *ch = new char[len + 1];
    memcpy(ch, str + len - step, step);
    memcpy(ch + step, str, len - step);
    memcpy(str, ch, strlen(ch));
}

7 标准输入输出通道包括cin, cout, cerr,对应于彪悍尊输入流,标准输出流,标准错误流。

8 输入一行字符串,找出其中出现的相同且长度最长的字符串,输出它及其首字符的位置。例如,“yyabcdabjcabceg”,输出结果为abc和3.

#include <iostream>
#include <cstring>
#include <utility>
#include <string>
#include <vector>
using namespace std;
vector<pair<int, string>> fun(const string& str)
{
    vector<string> subs;
    vector<pair<int, string>> res;
    int len = str.size();
    for (int i = 0; i < len; i++)
    {
        subs.push_back(str.substr(i));
    }
    int count = 1;
    int index = -1;
    int subLen = 1;
    string sub;
    //i为子串的长度
    for (int i = 1; i < len; i++)
    {
        for (int j = 0; j < len - 1; j++)
        {
            count = 1;
            for (int k = j + 1; k < len && subs[k].size() >= i; k++)
            {
                if (subs[k].substr(0, i) == subs[j].substr(0, i))
                {
                    count++;
                    break;
                }
            }
            if (count >= 2)
            {
                if (i > subLen)
                {
                    res.clear();
                }
                subLen = i;
                index = j;
                sub = subs[j].substr(0, i);
                res.push_back(make_pair(j, sub));
            }
        }
    }
    return res;
}
int main()
{
    string str;
    vector<pair<int, string>> res;
    while (cin>>str)
    {
        res = fun(str);
        for (auto it = res.begin(); it != res.end(); it++)
        {
            cout<<it->second<<":"<<it->first<<endl;
        }
    }
    return 0;
}

9 strstr(),返回值是主字符串中字符子串的位置以后的所有字符。(不能使用任何C程序已有的函数)

#include <iostream>
using namespace std;
const char* strstr1(const char *str, const char *charset)
{
    for (int i = 0; str[i] != '\0'; i++)
    {
        int j = i;
        int k = 0;
        while (str[j++] == charset[k++])
        {
            if (charset[k] == '\0')
            {
                return &str[i];
            }
        }
    }
    return NULL;
}
int main()
{
    char *str = "123456123";
    char *charset = "23";
    cout<<strstr1(str, charset)<<endl;
    return 0;
}

10 将一句话里面的单词进行倒置,标点符号不倒换。例如“i come from tianjian.”变成“tianjin. from come i”。

#include <iostream>
#include <cstring>
using namespace std;
void reverse(char *str, int left, int right)
{
    while (left < right)
    {
        char c = str[left];
        str[left++] = str[right];
        str[right--] = c;
    }
}
int main()
{
    char str[100];
    while (cin.getline(str, 100))
    {
        reverse(str, 0, strlen(str) - 1);
        int left = 0;
        for (int i = 0; i <= strlen(str); i++)
        {
            if (str[i] == ' ' || str[i] == '\0')
            {
                reverse(str, left, i - 1);
                left = i + 1;
            }
        }
        cout<<str<<endl;
    }
    return 0;
}

11 统计0到n之间1的个数

#include <iostream>
#include <cstring>
using namespace std;
int countOne(int n)
{
    int current = 0;
    int before = 0;
    int after = 0;
    int i = 1;
    int count = 0;
    while (n / i != 0)
    {
        current = n / i % 10;
        before = n / (i * 10);
        after = n - (n / i) * i;
        if (current > 1)
        {
            count += (before + 1) * i;
        }
        else if (current == 0)
        {
            count += before * i;
        }
        else if (current == 1)
        {
            count += before * i + after + 1;
        }
        i *= 10;
    }
    return count;
}
int main()
{
    int n;
    while (cin>>n)
    {
        cout<<countOne(n)<<endl;
    }
    return 0;
}

12 压缩字符串1233422222转化为1121324125.

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string str;
    while(getline(cin, str))
    {
        char c = str[0];
        int count = 1;
        string temp = "";
        for (int i = 1; i <= str.size(); i++)
        {
            if (str[i] != c || i == str.size())
            {
                temp += c;
                temp += count + '0';
                c = str[i];
                count = 1;
            }
            else
            {
                count++;
            }
        }
        cout<<temp.c_str()<<endl;
    }
    return 0;
}

设计模式与软件测试

1 自动化测试为何重要?

自动化测试可以让测试人员从枯燥无味的手工重复性测试中解放出来,并且提高工作效率,通过自动化测试结果来分析功能和性能上的缺陷。

2 描述一个测试结束的准则

一个测试结束的标准可以查看已提交的bug是否已经全部解决并已验证关闭,一般来说,bug验证率在95%以上,并且没有大的影响功能的bug处于未解决状态,就可以测试通过。

3 在一个测试计划中能包含哪些内容(如可用的人力资源)?

在一个测试计划中可以包含需要测试的产品的特点和主要功能模块,列出需要测试的功能点,并标明侧重点;测试的策略和记录(测试工具的确认,测试用例等文档模板,测试方法的确定);测试资源配置(确定测试每一阶段的任务和所需资源)。

4 请描述功能性测试和可用性测试之间的区别。

功能测试主要是黑盒测试,由测试人员进行,主要验证产品是否符合需求设计的要求;可用性测试主要是由用户(或者测试人员模拟用户行为)来进行的测试,主要是对产品的易用性进行测试,包括有效性(effectiveness)、效率(efficiency)和用户主观满意度(satisfication)。其中有效性指用户完成特定任务和达到特定目标时所具有的正确和完整程度;效率指用户完成任务的正确和完整程度与所使用资源(如时间)之间的比率;满意度指用户在使用产品过程中所感受到的主观满意和接受程度。

5 白盒测试

白盒测试有几种测试方法:条件覆盖、路径覆盖、语句覆盖、分支覆盖。其中分支覆盖又称判定覆盖,使得程序中每个判断的取真分支和取假分支至少经历一次,即判断的真假均曾被满足。

目录
相关文章
|
算法 Python 设计模式
学点PYTHON基础的东东--数据结构,算法,设计模式---观察者模式
按照小明明的设计模式抄抄看看。。 http://dongweiming.github.io/python-observer.html # 这个是观察者基类 class Subject(object): def __init__(self): self.
1146 0
|
算法 Python 设计模式
学点PYTHON基础的东东--数据结构,算法,设计模式---访问者模式
说实话,感觉不是特别多,可能没遇到过多场面, 所以对应用场景没感觉吧。 反正,各种模式就是把类的实例传来传去,久而久之,产生了一些规律。。。:) # 轮子,引擎, 车身这些定义好了都不需要变动 class Wheel: def __init__(self, name): self.
854 0
|
算法 Python 设计模式
学点PYTHON基础的东东--数据结构,算法,设计模式---单向链表
看来看来,还是以下这个实现最优雅。。 其它的,要么NODE冗余,要么初始化丑陋。。。 #!/usr/bin/env python # -*- coding: utf-8 -*- class Node: def __init__(self, initdata): self.
1194 0
|
25天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
116 9
|
16天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
22 1
|
3天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
22 5
|
18天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
21天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
23天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
47 4