2018.10.2 数据结构学习---双向链表的实现

/*
 * 学习时间:2018-10-2
 * 学习内容:数据结构之尾插法实现双向链表,以及链表的增删查改
 * 学习人:田超
 * QQ:770925351
 * Email:770925351@qq.com
 * 开发环境:Ubuntu 16.04 + CLion
 * */
#include <stdio.h>
#include <stdlib.h>

#define TRUE 1
#define FALSE 0
typedef struct DLNode   //定义循环链表结构体
{
    int data;
    struct DLNode *prior;
    struct DLNode *next;
}DLNode;

void createDList(DLNode *&L,int a[],int n)  //创建双向链表
{
    DLNode *s,*r;                           //s用于新建节点,r用于跟踪L的尾端节点
    int i;                                  //for循环临时变量
    L=(DLNode*)malloc(sizeof(DLNode));      //开辟循环链表头结点空间
    L->prior=NULL;                          //初始化头结点前驱指针
    L->next=NULL;                           //初始化头结点后继指针
    r=L;                                    //初始化r
    for(i=0;i<n;i++)
    {
        s=(DLNode*)malloc(sizeof(DLNode));  //开辟新节点空间
        s->data=a[i];                       //将数值赋值给新节点的数据域
        r->next=s;                          //将r指向的后继变成s
        s->prior=r;                         //将s指向的前趋设置为r
        r=s;                                //r移位,变成链表的最后一个节点
    }
    r->next=NULL;                           //此时r为最后一个节点,将它的后继指针变成NULL,创建链表结束
}

void travelNode(DLNode *L)
{
    DLNode *p=L->next;
    while(p!=NULL)
    {
        printf("data=%d\n",p->data);
        p=p->next;
    }
}

DLNode* findNode(DLNode *L,int x)           //在双向链表中查找x元素
{
    DLNode *p=L->next;                      //p为指向链表的指针,并初始化指向除头节点第一个节点
    while(p!=NULL)                          //当p不为空时候,一直循环
    {
        if(p->data==x)                      //比较值的大小,如果相等,则找到,立即结束循环
            break;
        p=p->next;                          //没有找到,p指向下一个节点
    }
        return p;                           //返回p指针,如果找到则会返回节点的地址,如果找不到,说明p到了最后一个节点,最后一个节点的next指针是NULL
}

void insertNode_tail(DLNode *L,int x)       //在双向链表中插入x元素,尾插法
{
    DLNode *p,*s;                           //s用来新建节点,p用来寻找当前链表中最后一个节点
    p=L->next;                              //初始化p节点
    while(p&&p->next!=NULL)                 //循环找到L的最后一个节点,并赋值给p,在这里判断的逻辑是:p不能为空且p的后继指针不能为空,当为空时候结束循环,说明p是最后一个节点
    {
        p=p->next;
    }
    s=(DLNode*)malloc(sizeof(DLNode));      //新建节点
    s->data=x;                              //将传进来的数据赋值给s
    if(p==NULL)                             //如果此时链表为空,则插入的节点是第一个节点
    {
        s->prior=L;                         //s节点的前趋指向头结点
        s->next=p;                          //s节点的后继指向p,也就是NULL
        L->next=s;                          //此时s作为链表的第一个节点,也就是L->next
    }
    else
    {
        s->prior=p;                         //将s的前趋指针指向p
        s->next=p->next;                    //将s的后继指针指向原先p的后继,因为p是最后一个节点,所以是NULL,如果说是中间的节点的话,就不是NULL
        p->next=s;                          //将p的后继重新指向s
    }

}

void insertNode_head(DLNode *L,int x)       //在双向链表中插入x元素,头插法
{
    DLNode *s;                              //s用来新建节点
    s=(DLNode*)malloc(sizeof(DLNode));      //为s开辟空间
    s->data=x;                              //将数据赋值给新建的节点s的data域
    s->prior=L;                             //将s的前趋指针指向头结点
    s->next=L->next;                        //将s的后继指针指向原先的第一个节点
    if(L->next==NULL)                       //如果此时链表为空,那么s节点将
        L->next=s;
    else
    {
        L->next->prior=s;                   //将原先第一个节点的前趋指针指向s
        L->next=s;                          //将头节点的next指针指向s
    }

}

int deleteNode(DLNode *L,int x)             //在双向链表中删除节点
{
    DLNode *p=L->next;                      //让p指向除头节点外的第一个节点
    while(p!=NULL)                          //循环至最后一个节点
    {
        if(p->data==x)                      //如果找到节点
        {
            if(p->next==NULL)               //如果找到的是最后一个节点
            {
                p->prior->next = p->next;   //将p节点前趋的后继指针指向NULL
                free(p);                    //释放空间
            }
            else
            {
                p->prior->next = p->next;   //将p节点前趋的后继指针指向p的后继
                p->next->prior = p->prior;  //将p节点后继的前趋指针指向p的前趋
                free(p);                    //释放空间
            }
            return TRUE;                    //返回真
        }
        p=p->next;                          //没有找到将p指向下一个节点
    }
    return FALSE;                           //循环结束,没有找到,返回假
}

int main()
{
    int a[5]={1,2,3,4,5};
    int n=5;
    DLNode *L;
    createDList(L,a,n);
    travelNode(L);
    printf("\n");
    deleteNode(L,5);
    travelNode(L);
    printf("\n");
    insertNode_tail(L,6);
    travelNode(L);
    printf("\n");
    insertNode_head(L,7);
    travelNode(L);
    printf("\n");
    return 0;
}

2018.10.04 数据结构学习---链栈的实现

/*
 * 学习时间:2018-10-4
 * 学习内容:数据结构之链栈的实现
 * 学习人:田超
 * QQ:770925351
 * Email:770925351@qq.com
 * 开发环境:Ubuntu 16.04 + CLion
 * */
#include <stdio.h>
#include <stdlib.h>

typedef struct LNode            //定义链栈节点结构体
{
    int data;
    struct LNode *next;
}LNode;

void initStack(LNode *&L)       //初始化栈
{
    L=(LNode*)malloc(sizeof(LNode));
    L->next=NULL;
    L->data=-1;
}

int isEmpty(LNode *L)           //判断是否栈空
{
    if(L->next==NULL)
        return 1;
    else
        return 0;
}

void push(LNode *L,int x)       //进栈,采用头插法
{
    LNode *p;
    p=(LNode*)malloc(sizeof(LNode));
    p->data=x;
    p->next=L->next;
    L->next=p;
    (L->data)++;
}

int pop(LNode *L,int &x)        //出栈,采用删除第一个节点
{
    LNode *p;
    if(isEmpty(L))
        return 0;
    p=L->next;
    x=p->data;
    L->next=p->next;
    (L->data)--;
    free(p);
    return 1;
}

int main()
{
    return 0;
}

  • 循环队列是顺序队的一种,意在解决队首和队尾指针同同时等于MAXSIZE-1的时候,会发声假上溢的情况,利用循环队列解决了这个问题
  • 判断是否队空,则看rear=front==0
  • 判断是否队满,则看(rear+1)%MAXSIZE==front
/*
 * 学习时间:2018-10-11
 * 学习内容:数据结构之循环队列的实现
 * 学习人:田超
 * QQ:770925351
 * Email:770925351@qq.com
 * 开发环境:Ubuntu 16.04 + CLion
 * */
#include <stdio.h>
#define MAXSIZE 10
#define TRUE 1
#define ERROR 0

typedef struct SqQueue
{
    int front;                  //队首
    int rear;                   //队尾
    int data[MAXSIZE];          //数据域
}SqQueue;

void initQueue(SqQueue &qu)     //初始化队
{
    qu.front=qu.rear=0;         //初始化队首
}

int isQueueEmpty(SqQueue qu)    //判断队是否为空
{
    if(qu.front==qu.rear)
        return TRUE;
    else
        return ERROR;
}

int isQueueFull(SqQueue qu)     //判断队是否已满
{
    if((qu.rear+1)%MAXSIZE==qu.front)
        return TRUE;
    else
        return ERROR;
}

int inQueue(SqQueue &qu,int x)  //入队
{
    if(isQueueFull(qu))
        return ERROR;

    qu.rear=(qu.rear+1)%MAXSIZE;
    qu.data[qu.rear]=x;

    return TRUE;
}

int outQueue(SqQueue &qu,int &x) //出队
{
    if(isQueueEmpty(qu))
        return ERROR;

    qu.front=(qu.front+1)%MAXSIZE;
    x=qu.data[qu.front];

    return TRUE;
}

int main() 
{
    return 0;
}

树莓派安装Ubuntu 16.04 实录

  • 树莓派3B+
  • Ubuntu 16.04 arm版本

镜像由"树莓派爱好者基地"发布,特此感谢

下载地址:https://pan.baidu.com/s/1XNBQH7uaegaZThz3OO7i_Qj

  • 读卡器1个
  • 32G 内存卡一张
  • 操作环境 Ubuntu 16.04

  1. 写入镜像
sudo dd bs=4M if=2018-06-28-ubuntu-1604-aarch64.img of=/dev/sd? "?指的是你插入的内存卡在Ubuntu中是那个代号,请自行查看"
  1. 远程登录
#用户名:pi 密码:raspberry
ssh pi@ip地址
  1. 安装配置终端中文环境
sudo apt-get install language-pack-zh-hans -y
  1. 安装桌面环境
sudo apt-get install --no-install-recommends xserver-xorg -y
sudo apt-get install mate-desktop-environment-core lightdm -y
  1. 开启硬件加速
sudo vim /boot/config.txt # 在文件末尾加上以下两句
dtoverlay=vc4-fkms-v3d
gpu_mem=256
  1. 配置VNC服务
sudo apt install x11vnc -y # 安装vnc服务
sudo x11vnc -storepasswd /etc/x11vnc.pass # 设置访问密码

[Unit]
Description=Start x11vnc at startup.
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/bin/x11vnc -auth guess -forever -loop -noxdamage -repeat -rfbauth /etc/x11vnc.pass -rfbport 5900 -shared
[Install]
WantedBy=multi-user.target

sudo systemctl enable x11vnc # 开机启动VNC服务
sudo systemctl start x11vnc # 开启VNC服务
  1. 安装配置桌面中文环境
sudo apt-get install language-selector-gnome -y #安装Language Support

# 1.远程链接VNC至树莓派
# 2.进入Application->Personal->Language Support 
# 3.出现以下图片,这是我已经安装好语言,正常应该是英文的,会自动弹出对话框,如图二,点击install
# 4.安装好中文语言包之后进行下面修改配置文件的步骤

Language Support

语言支持

sudo vim /etc/default/locale #将原先内容注释,添加以下内容

LANG="zh_CN.UTF-8"
LANGUAGE="zh_CN:zh"
LC_NUMERIC="zh_CN"
LC_TIME="zh_CN"
LC_MONETARY="zh_CN"
LC_PAPER="zh_CN"
LC_NAME="zh_CN"
LC_ADDRESS="zh_CN"
LC_TELEPHONE="zh_CN"
LC_MEASUREMENT="zh_CN"
LC_IDENTIFICATION="zh_CN"
LC_ALL="zh_CN.UTF-8"

sudo vim /etc/environment # 将以下内容添加到文件中
LANG="zh_CN.UTF-8"
LANGUAGE="zh_CN:zh"
LC_NUMERIC="zh_CN"
LC_TIME="zh_CN"
LC_MONETARY="zh_CN"
LC_PAPER="zh_CN"
LC_NAME="zh_CN"
LC_ADDRESS="zh_CN"
LC_TELEPHONE="zh_CN"
LC_MEASUREMENT="zh_CN"
LC_IDENTIFICATION="zh_CN"
LC_ALL="zh_CN.UTF-8"
reboot # 重启机器
  1. 安装add-apt-repository
sudo apt-get install software-properties-common -y

近日,物联网界面设计课程进入验收阶段,其中二维码生成阶段有些许难搞,在我经过多方查阅资料,终于解决了这一难题,现在将方法分享给大家,话不多说,干货奉上。
qrencode源码下载地址

  • 进入上面的地址下载qrencode源码,解压缩之后,将文件夹中的config.h.in改名为config.h,然后将所有的.c和.h文件引入到你的工程之中。
  • 修改config.h,重新定义 MAJOR_VERSION、MICRO_VERSION、MINOR_VERSION、VERSION,重新定义的方法:找到#undef MAJOR_VERSION位置,在其下面定义#define MAJOR_VERSION 1,其他几个也这么定义。
  • 在QT的pro文件中添加DEFINES += HAVE_CONFIG_H 定义全局宏定义,然后编译,你的工程就可以用二维码相关的库了,下面贴出相关函数:
QPixmap OBSBasic::createQRCode(const QString &text)
{
 int margin = 2;
 if (text.length() == 0)
 {
  return QPixmap();
 }
 QRcode *qrcode = QRcode_encodeString(text.toLocal8Bit(), 2, QR_ECLEVEL_L, QR_MODE_8, 0);
 if (qrcode == NULL) {
  return QPixmap();
 }
 unsigned char *p, *q;
 p = NULL;
 q = NULL;
 int x, y, bit;
 int realwidth;

 realwidth = qrcode->width;
 QImage image = QImage(realwidth, realwidth, QImage::Format_Indexed8);
 QRgb value;
 value = qRgb(255, 255, 255);
 image.setColor(0, value);
 value = qRgb(0, 0, 0);
 image.setColor(1, value);
 image.setColor(2, value);
 image.fill(0);
 p = qrcode->data;
 for (y = 0; y<qrcode->width; y++) {
  bit = 7;
  q += margin / 8;
  bit = 7 - (margin % 8);
  for (x = 0; x<qrcode->width; x++) {
   if ((*p & 1) << bit)
    image.setPixel(x, y, 1);
   else
    image.setPixel(x, y, 0);
   bit--;
   if (bit < 0)
   {
    q++;
    bit = 7;
   }
   p++;
  }
 }
 return QPixmap::fromImage(image.scaledToWidth(200));
}

想要显示二维码,就在界面添加一个label,直接调用函数:

label->setPixmap(createQRCode(字符串));

  1. 2/2 -->>整数/整数 得到浮点数类型 float类型
  2. 2//2 -->整数//整数 得到整数类型 int类型[[]]
  3. 0b 二进制
  4. 0x 十六进制
  5. 0o 八进制
  6. bin()转换为二进制
  7. type()查看数据类型
  8. int()转换为十进制
  9. hex()转换为十六进制
  10. oct()转换为八进制
  11. 0表示为假False,其他数字都表示为真True
  12. [] 列表
  13. {} 字典
  14. 用 单引号' ' 双引号" " 三引号''' '''表示字符串
  15. 三引号'''可以支持str字符串换行
'''
hello world
hello world
'''
  1. 单引号与双引号也可以实现字符串换行,但是必须得加上\
  2. 转义字符

n 换行

\' 单引号

\t 横向制表符

\r 回车

  1. 当一个字符串前加上一个r,代表是原始字符串

例如: r'hello n world'

  1. 字符串的运算

+ 拼接字符串

* 多次重复字符串

字符串[序号] 访问字符串的序号个字符

注:序号从0开始,-n代表倒数第n个字符

[0:n] 从第一个到第n个字符

[n:]从第n个字符到最后

[-n:]从倒数第n个字符到最后

  1. 列表(list)

[元素,元素,元素]

  1. 访问列表元素

[元素,元素,元素][序号]

注:[元素,元素,元素][序号] 返回的是元素数据类型

[元素,元素,元素][序号:](带有截取符号)返回的是列表类型

  1. 列表操作

+ 合并列表

* 重复列表

  1. 元组(tuple)

(元素,元素,元素),其他操作与列表相似

注:当元组中只有一个元素时候,返回数据类型为这个元素的数据类型

定义一个元素的元组: (元素,)

定义一个空元组: ()

  1. 集合(set)

{元素,元素,元素}

特点: 无序,不重复 切片和序列都不支持

- 集合A - B表示剔除A中含有的B中的元素

& 集合A & B表示A和B的交集,返回一个集合类型

| 集合A | B表示A和B的并集,返回一个集合类型

  1. 字典(dict)

{key1:value1,key2:value2,key3:value3....}

通过key来访问value

{}[key]

key是唯一的

key必须是不可变的类型(int,str)

数据类型总结

  1. str list tuple 序列

数据类型 [序号] 返回相应序号位置的元素

* + 运算

  1. 切片 :
  1. kaitouin 和 not in

判断元素是否存在

  1. 常用函数

max()

min()

id()显示某一个变量在内存中的地址

len() 数据类型 长度

ord() 转换为 ascII 码

变量与运算符

  1. 命名规则
  • 不能用保留关键字
  • 字母、下划线、数字组成,但是不能由数字开头
  • 变量名区分大小写
  1. 比较有意思的几种情况
a = 1
b = a
a = 3
print(b)
1
a = [1,2,3,4,5]
b = a
a[0] = '1'
print(b)
['1',2,3,4,5]

造成以上两种情况的原因:

int str tuple 值类型(不可变) list dict set 引用类型(可变)

引用类型的数值是可以改变的,值类型是不可变的.

  1. 运算符

+ 连接,相加

* 重复

- 相减

/ 相除

// 整除

% 求余数

** 乘方

is 与 ==的关系

==比较值相等 is比较身份是否相等

isinstance(变量,(类型,类型,类型)),判断变量和类型是否一致

  1. range(a,b)

生成从a到b的序列

  1. range(a,b,c)

a到b的序列,c为步长,就是每两个元素的距离

  1. 如何让一个文件夹成为一个包

文件夹下新建文件__init__.py,而且这个文件的模块名称是包的名字,即是文件夹的名字

  1. 将包别名

import 包 as 别名

  1. 需要引入包中的某一个变量或者函数的方法

from 包.模块 import 变量名,函数名

例子(导入t包中c7模块中的a变量):
from t.c7 import a

  1. 系统常用的内库

sys
datetime
io

函数

  1. round()函数

保留指定小数位数

例如:

a = 1.234

round(a,2) = 1.23

  1. Python函数的返回值默认为none
  2. Python函数返回多个返回值,最后返回的是一个元组类型
  3. 序列解包

a,b,c = 1,1,1

或者 d = (1,1,1)

a,b,c = d

  1. 必须参数与关键字参数

必须参数:是指函数的参数必须按照顺序传进去,例如:add(x,y),你如果这样传的话add(1,2),x=1,y=2

关键字参数:是指指定每个参数是啥,例如add(x,y) add(x = 1,y = 2)

  1. 默认参数

默认参数是指在定义函数时候就把参数的值定义,在调用函数的时候,如果不传进去参数的话,那么参数的值就是预定义时候的值

例:

def print_stu_message(name,age=18,college="北华航天工业学院"):
    print("我叫:"+name+"我的年龄是:"+str(age)+"我的大学是:"+college)
print(print_stu_message("tianchao"))
  1. 函数和方法的区别

方法:设计层面
函数:程序运行、过程式的一种概念

  1. 局部变量与全局变量

局部变量与全局变量同名时不会覆盖全局变量,一旦出去函数作用域,全局变量立马恢复原先值

  1. 几个常用的函数

title() 将英文单词第一个字母大写

  1. 函数中传递列表,如果不想修改列表的值,请这样传列表名[:]
  2. 函数中想传入不限数量个参数,def 函数名(*变量名),在函数调用的时候,传入多个参数,函数会自动将这些参数封装成一个元组,利用元组去访问各个参数
  3. 函数中使用任意数量的关键字实参

def 函数名(**变量名)

# 使用任意数量的关键字实参

def student(**infos):
    infomation = {}
    for key,value in infos.items() :
        infomation[key] = value

    return infomation

print(student(name='tianchao',age='23',college='NCIAE'))
{'name': 'tianchao', 'college': 'NCIAE', 'age': '23'}

定义:

class 类名():

​ 对象

​ 函数等等

构造函数:

def __init__()

类内部的方法需要使用自己的对象内部定义的变量的话,需要引入self关键字,调用方式为:self.变量

  1. 继承

Python3:

class 类名(父类名):
    def __init__(self,变量,变量):
        super().__init__(变量,变量)

Python2.7:

class 类名(父类名):
    def __init__(self,变量,变量):
        super(类名,self).__init__(变量,变量)
  1. OrderedDict()类

初始化一个有顺序的字典

  1. 驼峰命名法

类名中的每个单词的首字符都大写,而不使用下划线.实例名和模块名都采用小写格式,并在单词之间加上下划线

文件和异常

  1. 读入整个文件
  • open("文件路径")
  • open()返回一个表示文件的对象,下方的with表示系统会自动在合适的时候去关闭文件,以避免文件数据的丢失
  • 这样读取,结果会多输出一个空行,原因是:read()到达文件末尾时候会返回一个空字符串,而将这个空字符串显示出来就是一个空行
  • 这个空行可以用方法rstrip()去进行删除
with open("./pi.txt") as file:
#file = open("./pi.txt") (也可以这么定义)
    contents = file.read()
    print(contents)
3.1415926535
  8979323846
  2643383279
  1. 逐行读取
  • 利用for循环语句
  • 输出结果会出现空行,是因为每一行都会有一个n换行符,所以输出出来就会有空行
  • 空行可以用方法rstrip()去进行删除
with open("./pi.txt") as file:
    for line in file :
        print(line)
  1. 将文件读出来的每行内容保存成一个列表
  • readlins()
  • 多余的空格可以用strip()删除

  1. open("./pi.txt") as file:
    lines = file.readlines()
    print(lines)

  1. replace(原单词,替换单词)

功能:替换字符串中的特定单词

  1. 异常
  • ZeroDivisionError 异常处理

这种异常是在0作为被除数时候会出现,一般解决异常的思路就是用try-except代码块

try:
    pass
except 异常名称:
     pass
  • try-except-else代码块

工作原理:Python尝试执行try代码块中的代码,而且只有try代码块中存在的代码才可能引发异常,except代码块中的代码表示如果出现某种异常时候系统该怎么办,而else代码块中的代码是try代码块中的代码执行成功之后才能运行

try:
   pass
except 异常名称:
   pass
else:
    pass
  • FileNotFoundError异常

找不到文件会出现这种异常

  • split()函数

将字符串按照空格分隔成n个元素,并将这些元素存储在列表中,可以用于去统计一篇文章的单词数量

  • count()函数

计算字符串中字串的个数

  • lower()函数

将字符串全部转换为小写格式

  1. 存储数据

JSON

json模块可以使Python创建json格式文件,下面介绍两个函数,一般使用的时候需要在文件中导入json模块

import json
  • json.dump()

json.dump(参数一,参数二)

参数一是指你需要写入的内容,参数二是你要写入的文件对象名字

示例代码:

numbers = [2,4,6,8,10]
filename = "number.json"
with open(filename,"w") as file :
    json.dump(numbers,file)
  • json.load()函数

功能是读取json格式文件内容

使用方法是json.load(文件名),这将返回一个字符串

with open(filename) as file :
   content = json.load(file)

测试代码

  1. 单元测试

核实函数某个方面没有问题

  1. 测试用例

一组单元测试,核实函数在各种情形下的行为都符合要求

  1. 全覆盖测试

全部测试一遍,了解即可

  1. 测试流程
  • 定义类名,一般都是XXXXTestCase
  • 导入unittest模块
  • 定义的类需要继承unittest下的TestCase类
  • 在类中定义以test_开头的方法,一般后面的命名会是你自己测试的函数名称,参数传self
  • setUp(self)函数

一般在测试类方法中使用,由于测试每一个方法都需要实例化一个类,所以,在自己写的类中重写setUp(self)方法,把实例化类的重复语句写入进去,有效较少代码量和规范语句结构

  • 测试流程

unittest.main()方法执行之后,Python先执行setUp(self)方法,然后执行每个以test_开头的测试方法

  • 自己定义的方法需要调用断言方法来判断函数功能是否达到自己预期
方法用途
assertEqual(a,b)核实 a==b
assertNotEqual(a,b)核实 a!=b
assertTrue(x)核实 x为True
assertFalse(x)核实 x为False
assertIn(item,list)核实 item在list中
assertNotIn(item,list)核实 item不在list中
  • 示例代码
import unittest #导入unittest模块
#函数的作用是将名字和姓名连接在一起,并且将名字和姓名都格式化成第一个字母大写的形式
def get_name(first,last):
   full_name = first + " " + last
   return full_name.title()

#对函数进行测试

#定义类
class NamesTestCase(unittest.TestCase):            #继承unittest的TestCase类
   def test_first_last_name(self):             #定义方法
       full_name = get_name('tian','chao')        #调用测试方法
       self.assertEqual(full_name,'Tian Chao') #调用断言方法判断函数是否达到预期
unittest.main()
  • 输出结果
.
----------------------------------------------------------------------
> Ran 1 test in 0.000s
> 
> OK
> ```