C++算法竞赛:类,结构体,指针,引用,链表的基本知识

目录

一、类与结构体

1.类的使用:

2.构造函数:

二、指针

1.程序在操作系统里运行的时候的一些基本概念

2. 指针和引用

三、链表

 1.链表的表示

 2.链表的建立

3.添加元素 

4.删除元素 


一、类与结构体

1.类的使用:

类中的变量和函数被统一称为类的成员变量。

private后面的内容是私有成员变量,在类的外部不能访问;public后面的内容是公有成员变量,在类的外部可以访问。

#include using namespace std;
const int N = 1000010;
class Person
{
    private:
        int age, height;
        double money;
        string books[100];
    public:
        string name;
        void say()
        {
            cout << "I'm " << name << endl;
        }
        int set_age(int a)
        {
            age = a;
        }
        int get_age()
        {
            return age;
        }
        void add_money(double x)
        {
            money += x;
        }
} person_a, person_b, persons[100];
int main()
{
    Person c;
    c.name = "yxc";      // 正确!访问公有变量
    c.age = 18;          // 错误!访问私有变量
    c.set_age(18);       // 正确!set_age()是共有成员变量
    c.add_money(100);
    c.say();
    cout << c.get_age() << endl;
    return 0;
}

 结构体和类的作用是一样的。不同点在于类默认是private,结构体默认是public。

但是在习惯上,我们一般把只有数据的,而且函数少的,一些短的与数据相关的把它打包成结构体,把一些复杂的,含义混乱的,代码比较长的把它打包成类。

2.构造函数:

1.这样写只有一种写法:

struct person
{
	int age, height;
	double money;
	person(int _age, int _height, double _money)//构造函数 函数名与结构体名一样
	{
		age = _age;
		height = _height;
		money = _money;
	}
};
int main()
{
	person p(18, 180, 100.0);//这样写必须要有参数。
	//person p()这样写会报错
	return 0;
}

 2.由1.可得应该要加上person(){};

struct person
{
	int age, height;
	double money;
	person() {}
	person(int _age, int _height, double _money)//构造函数 函数名与结构体名一样
	{
		age = _age;
		height = _height;
		money = _money;
	}
};
int main()
{
	//person p(18, 180, 100.0);//这样写必须要有参数。
	person p();//这样写就不会报错
	return 0;
}

 3.易错情况:构造函数的参数个数要与主函数里输入的参数个数相匹配。否则就只有一个0参数的构造函数。

这样会报错:

struct person
{
	int age, height;
	double money;
	person() {}//这里0个参数
	person(int _age, int _height, double _money)//这里是3个参数
	{
		age = _age;
		height = _height;
		money = _money;
	}
};
int main()
{
	person p(18, 180);//2个参数。易错情况:以为会自动放到3个参数的函数里,money的值自动赋0
	cout << p.money;//这里就会报错,因为在结构体里只有0或3个参数的
	return 0;
}

这样不会报错,money会输出结果(但我在VS2022里的结果很小,不为0,y总上课输出的结果为0):

struct person
{
	int age, height;
	double money;
	person() {}//这里0个参数
	person(int _age, int _height){}
	person(int _age, int _height, double _money)//这里是3个参数
	{
		age = _age;
		height = _height;
		money = _money;
	}
};
int main()
{
	person p(18, 180);//2个参数。易错情况:以为会自动放到3个参数的函数里,money的值自动赋0
	cout << p.money;//这里就会报错,因为在结构体里只有0或3个参数的
	return 0;
}

4. 构造函数里赋值的简便写法,这样运行速度也更快些

在写构造函数之前一般先要加个public:

struct person
{
	int age, height;
	double money;
	person() {}//这里0个参数
	person(int _age, int _height){}
	person(int _age, int _height, double _money):age(_age),height(_height),money(_money){}
};

二、指针

1.程序在操作系统里运行的时候的一些基本概念

每一个程序都有一个进程,每一个代码写好后点击运行都会弹出一个黑框,这个黑框就是一个进程。每个进程之间都是完全独立的。开数组需要空间,而空间指的是内存,平常所说的内存有4GB,8GB,16GB等。比如一个8GB的电脑,每个进程的空间大小就是8GB,就好像一个从0到8GB的数组,是用16进制编码,用0或1来表示。现在电脑一般是64位,但实际运行的时候并没有达到64,但一定大于32。

寻找一个变量地址(用两种方法写):

局部变量

#define _CRT_SECURE_NO_WARNINGS
#include #includeusing namespace std;
int main()
{
	char c = 'a';
	//两种表示方式:
	cout << (void*)&c << endl;
	printf("%p\n", &c);
	char d;
	cout << (void*)&d << endl;
	return 0;
}

输出结果:

0x7fff7c0df9ce

0x7fff7c0df9ce

0x7fff7c0df9cf

表明:局部变量在栈里面是从上往下存的。

全局变量:

#define _CRT_SECURE_NO_WARNINGS
#include #includeusing namespace std;
char a, b;//全局
int main()
{
	char c = 'a';
	//两种表示方式:
	cout << (void*)&c << endl;
	printf("%p\n", &c);
	char d;
	cout << (void*)&d << endl;
	cout << (void*)&a << endl;
	cout << (void*)&b << endl;
	return 0;
}

 结果:

0x7fff7c0df9ce

0x7fff7c0df9ce

0x7fff7c0df9cf

0x60191

0x60192

表明:全局变量在堆里面是从下往上存的。

2. 指针和引用

指针指向存放变量的值的地址。因此我们可以通过指针来修改变量的值。

int main()
{
	int a = 10;
	int* p = &a;//这里的*是指一个int类型的指针变量
	cout << *p << endl;//这里的*是找到a的地址然后得到a的数值
	*p = 5;
	cout << a << endl;//也能够通过地址来修改数值
	return 0;
}

输出结果:10  5

以下这种在算法里不怎么用,所以没用详细讲。其中指针的用法难就难在多颗*的使用。

int main()
{
	int a = 10;
	int* p = &a;//这里的*是指一个int类型的指针变量
	int** q = &p;//通过q找到p的地址,p是一颗*,q就得再加一颗*
	cout << (void*)q << endl;
	return 0;
}

数组名是一种特殊的指针。指针可以做运算,a+1不是指数值加1,而是指加1个变量长度,下面例子是int类型。所以a+1是加了4个字节,a+2是加了8个字节。

int main()
{
    int a[5] = {1, 2, 3, 4, 5};
    for (int i = 0; i < 5; i ++ )
        cout << *(a + i) << endl;
    return 0;
}
int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int* p = &a[0];
    int* q = &a[1];
    cout << q - p;
    return 0;
}

 输出结果:1

 数组名其实就是数组里第一个变量的地址,本质就是一个指针。并且整个数组在内存里是顺序连续存储的。

int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    cout << a << endl;
    for (int i = 0; i < 5; i++)
    {
        cout << (void*)&a[i] << endl;
    }
    return 0;
}

运行结果: int占4个字节,所以相邻两个地址相差4。

0000008CB20FFC78

0000008CB20FFC78

0000008CB20FFC7C

0000008CB20FFC80

0000008CB20FFC84

0000008CB20FFC88

引用:

int main()
{
    int a = 10;
    int* p = &a;//c的指针
    int& q = a;//引用,是c++写法里对c的指针的简化。q相当于a的别名
    return 0;
}

三、链表

 1.链表的表示

struct Node
{
    int val;
    Node* next;//可以在结构体里面定义一个自己的指针,这是一个递归,但不可以定义一个变量。
    //Node next;就不可以,这样会报错。
    //第一种定义方式:
    Node(int _val) :val(_val),next(NULL){}
} *head;
int main()
{
    //第一种定义方式:
    Node node = Node(1);//这样表明定义了一个Node变量
    Node* p = new Node(1);//一般喜欢这样写,用指针写,生成一个结构体。
    //不加new时,指生成了一个Node变量,加new指不仅生成了一个Node变量,且返回值是其的地址,用时加new
    //还可以写auto p = new Node(1);因为知道返回值类型是Node*
    p->next = p;//就使p指向了自己
    //p是指针,所以调用时用-> 当为变量时,调用用. eg:Node q=Node(1);q.next=q;
    return 0;
}

 2.链表的建立

代码如下:

struct Node
{
    int val;
    Node* next;//可以在结构体里面定义一个自己的指针,这是一个递归,但不可以定义一个变量。
    //Node next;就不可以,这样会报错。
    //第一种定义方式:
    Node(int _val) :val(_val),next(NULL){}
} *head;
int main()
{
    //第一种定义方式:
    Node* p = new Node(1);
    Node* q = new Node(2);
    Node* o = new Node(3);
    p->next = q;
    q->next = o;
    o->next = NULL;//指向空
    for (Node* r = p; r; r = r->next)
    {
        cout << r->val << ' ';
    }
    return 0;
}

输出结果:1 2 3

3.添加元素 

在算法中用的最多的是在链表前面加一个元素。

struct Node
{
    int val;
    Node* next;//可以在结构体里面定义一个自己的指针,这是一个递归,但不可以定义一个变量。
    //Node next;就不可以,这样会报错。
    //第一种定义方式:
    Node(int _val) :val(_val),next(NULL){}
} *head;
int main()
{
    //第一种定义方式:
    Node* p = new Node(1);
    Node* q = new Node(2);
    Node* o = new Node(3);
    p->next = q;
    q->next = o;
    o->next = NULL;//指向空
    //头插法增加元素:
    Node* head = p;
    Node* u = new Node(4);
    u->next = head;
    head = u;//此时head应该指向u
    //打印链表:
    for (Node* r = head; r; r = r->next)
    {
        cout << r->val << ' ';
    }
    return 0;
}

输出结果:4 1 2 3 

4.删除元素 

只需找到要删除元素的前一个元素的位置,让前面的这一个元素的next直接指向删除元素的下一个元素,跳过需要删除的元素,这样需要删除的元素就不会打印出来了。

以删除2为例:已经知道2的前一个元素是head

int main()
{
    //第一种定义方式:
    Node* p = new Node(1);
    Node* q = new Node(2);
    Node* o = new Node(3);
    p->next = q;
    q->next = o;
    o->next = NULL;//指向空
    Node* head = p;
    head->next = head->next->next;//删除2
    //打印链表:
    for (Node* r = head; r; r = r->next)
    {
        cout << r->val << ' ';
    }
    return 0;
}

 参考y总的语法基础课,这是我的笔记。习题没加在里面。