暑假資讀[2]

陳仲肯

自我介紹

  • 209

  • 資讀真正最弱的

  • kennyfs

指標

資料的路標

可以幹嘛?

  • 傳參數給函數,還能修改
  • 加快效率
  • 動態資料結構

基本操作

  • * 取值:指標變成值

  • & 取址:值變指標

  • 宣告:在變數名字前加一個*

  • 指標可以加減喔

int *pointer;
int* a,b;//a是指標,b是值
int *a,*b;//ab都是指標
int a;
int *pa=&a;
cout<<&a<<'\n';
cout<<pa<<'\n';

0x7ffdbddd9e3c

0x7ffdbddd9e3c

int a[10]={1,2,3,4,5,6,7,8,9,10};
int *p=a;//=&a[0]
cout<<a[5]<<'\n';
cout<<*(p+5)<<'\n';

6

6

指標和陣列的關係

int a[10]={1,2,3,4,5,6,7,8,9,10};
for(int i=0;i<10;++i){
    cout<<a[i]<<'\n';
}
for(int *p=a;p!=a+10;++p){
    cout<<*p<<'\n';
}

加快效率

int a[10]={1,2,3,4,5,6,7,8,9,10};
int *p=a;
cout<<a[1000]<<'\n';
cout<<*(p+1000)<<'\n';
cout<<*(p+7122)<<'\n';
cout<<*(p+8787)<<'\n';

Segmentation fault

或讀到沒用的值

參考

別名

變數的綽號

可以幹嘛?

  • 傳參數給函數,還能修改
  • 加快效率
  • 比起指標可以不用*和&

基本操作

  • 宣告:在變數名字前加一個&
  • 當成一般的變數使用
int a=5;
int &b=a;
a=3;
cout<<a<<' '<<b<<'\n';
b=7122;
cout<<a<<' '<<b<<'\n';

3 3

7122 7122

不要搞混

  • 用指標時的&:取址

  • 用參考時的&:宣告時要用

指標的指標?

指標的參考?

你可能會看到這樣的東西

int a=5;
int *p=&a;
int *&b=p;
int c,d;
p=&c;
cout<<p<<' '<<b<<'\n';
b=&d;
cout<<p<<' '<<b<<'\n';

莫急莫慌莫害怕

int a=5;
int *p=&a;//指標
int *&b=p;//指標的參考
int c=7,d=8;
p=&c;
cout<<*p<<' '<<*b<<'\n';
b=&d;
cout<<*p<<' '<<*b<<'\n';

7 7

8 8

函數

函數?函式?

都可以

用途

  • 取代重複出現很多次的程式
  • 遞迴
int a[10]={1,2,3,4,5,6,7,8,9,10};
int b;
b=a[0]*a[5];
b=a[6]*a[3];
b=a[7]*a[1];
b=a[2]*a[2];
b=a[8]*a[7];
b=a[9]*a[5];

當你一直做重複的事你就會覺得很煩

一些名詞

  • 呼叫:執行函數
  • 回傳:函數執行完,回到呼叫那個函數的地方

(有可能伴隨著值)

函數的宣告

回傳值型態 函數名字(參數型態 參數名字,參數型態 參數名字...){
    做事情
}

回傳值型態

  • int,float,double:回傳這些型態的值,一定要回傳
  • void:不回傳值,要回傳的話就:
return;
return 變數;
void hello(){
    cout<<"Hello\n";
}
int one(){
    cout<<"1\n";
    return 1;
}
void hello(){
    cout<<"Hello\n";
}
int main(){
    hello();
}
void num(int n){
    cout<<n<<'\n';
}
int main(){
    int a=3;
    num(5);
    num(a);
}

呼叫函數

處理回傳值

int one(){
    return 1;
}
int main(){
    int a;
    a=one();
    cout<<a<<'\n';
    cout<<one()<<'\n';
}
int a[10]={1,2,3,4,5,6,7,8,9,10};
int multiply(int first,int second){
    return a[first]*a[second];
}
int main(){
    int b;
    b=multiply(0,5);
    b=multiply(6,3);
    b=multiply(7,1);
    b=multiply(2,2);
    b=multiply(8,7);
    b=multiply(9,5);
}

就可以把原本的程式寫成這樣

雖然現在沒有比較短

但是當函數裡的程式有十幾行就會短很多了

全域變數、區域變數?

#include <iostream>
using namespace std;
int hi;
int main(){
}
#include <iostream>
using namespace std;
int main(){
    int sum=0;
    for(int i=0;i<5;++i){
        sum+=i;
        cout<<sum<<'\n';
    }
}

全域變數、區域變數?

#include <iostream>
using namespace std;
void printsum(){
    cout<<sum<<'\n';
}
int main(){
    int sum=0;
    for(int i=0;i<5;++i){
        sum+=i;
        printsum();
    }
}

全域變數、區域變數?

#include <iostream>
using namespace std;
int sum=0;
void printsum(){
    cout<<sum<<'\n';
}
int main(){
    for(int i=0;i<5;++i){
        sum+=i;
        printsum();
    }
}

全域變數、區域變數?

#include <iostream>
using namespace std;
int sum=0;
void printsum(){
    cout<<sum<<'\n';
}
int main(){
    for(int i=0;i<5;++i){
        char a='a';
        sum+=i;
        printsum();
    }
    cout<<"i="<<i<<'\n';
    cout<<"a="<<a<<'\n';
}

全域變數、區域變數?

#include <iostream>
using namespace std;
int sum=0;
void printsum(){
    cout<<sum<<'\n';
}
int main(){
    int i;char a;
    for(i=0;i<5;++i){
        a='a';
        sum+=i;
        printsum();
    }
    cout<<"i="<<i<<'\n';
    cout<<"a="<<a<<'\n';
}

全域變數、區域變數?

傳給函數的參數也是區域變數

陣列可以開多大?

以int為例

  • 全域變數:536867927

  • 區域變數:2095884

5\times 10^8
2\times 10^6

2GB

8MB

參數型態差異

int f(int a)	//pass by value		//傳值
int f(int* a)	//pass by pointer	//傳指標
int f(int& a)	//pass by reference	//傳參考
傳值 傳指標 傳參考
作法 呼叫時複製一份 直接丟指標 直接丟參考
在函數中修改
會不會影響
原本的值
不會
f(b);
void f(int* a){
  *a=5;
}
int main(){
  int a=87;
  cout<<a<<'\n';
  f(&a);
  cout<<a<<'\n';
}

87

5

void f(int& a){
  a=5;
}
int main(){
  int a=87;
  cout<<a<<'\n';
  f(a);
  cout<<a<<'\n';
}

87

5

做的事一模一樣但參考比較好寫

多個名字一樣的函數?

int j(){
    return 3;
}
int j(int a){
    return a+5;
}
int j(char c){
    return c-'0';
}
int main(){
    cout<<j()<<'\n';
    cout<<j(8)<<'\n';
    cout<<j('3')<<'\n';
}

會自動配對!

傳入陣列

int sum(int list[],int len){
    int ans=0;
    for(int i=0;i<len;++i)ans+=list[i];
    return ans;
}
int main(){
    int a[10]={1,2,3,4,5,6,7,8,9,10};
    cout<<sum(a,10);
}

傳入陣列

int sum(int* list,int len){
    int ans=0;
    for(int i=0;i<len;++i)ans+=list[i];
    return ans;
}
int main(){
    int a[10]={1,2,3,4,5,6,7,8,9,10};
    cout<<sum(a,10);
}

其實相當於傳入第一格的指標,不是複製一份陣列

但是通常陣列都會宣告為全域變數,所以很少用

遞迴

recursion

自己呼叫自己

int pow(int a,int b){
    //return a^b
    //b>=0
    if(b==0)return 1;
    return a*pow(a,b-1);
}
a^b=a\times a^{b-1}

飯粒

遞迴的要素

  • 終止條件
  • 終止時要幹嘛
  • 要怎麼遞迴
a^b=a\times a^{b-1}

b=0

回傳a^0=1

分析複雜度

  • 每次呼叫O(1)
  • 總共呼叫O(b)次
  • 答:O(b)
pow(a,b)

例題

優點

  • 寫起來很直觀

  • 輕鬆處理複雜問題

缺點

  1. 比直接用迴圈寫慢,因為要呼叫函數

  2. 佔記憶體,因為要儲存目前函數的狀態(參數、區域變數)

BTW

兩個函數互相呼叫也算遞迴

比如暴力枚舉下棋的所有可能:

黑棋函數:枚舉棋所有可能

白棋函數:枚舉棋所有可能

例題,有點難
 

struct、class

結構、類別

通常解題用不到class

因為解題不需要用物件導向的概念

struct是什麼

自訂型態

物件、實體?

  • 物件:你自訂的型態

  • 實體:用這個型態產生的變數

struct a{
...
};
a b;

struct由什麼組成?

屬性(變數)

方法(函數)

(包含初始化、回收)

語法

struct 結構名字{
    變數型態 變數名字;//(屬性)
    變數型態 變數名字,變數名字;
    函數
}實體名字;//兩種宣告都可以,沒有要宣告就直接分號
結構名字 實體名字;
  • 結構中的函數存取自己的變數、函數可以直接用

  • 在外面存取實體的變數、函數要用"."

  • 另外,物件指標存取變數、函數要用"->"

dot *a;
cout<<a->x;

例子

struct dot{
    int x,y;
    dot(){
        x=y=0;
    }
    void move(int dx,int dy){
        x+=dx;
        y+=dy;
        cout<<"In dot.move:"<<x<<' '<<y<<'\n';
    }
};
int main(){
    dot a;
    a.move(1,1);
    cout<<"In main:"<<a.x<<' '<<a.y<<'\n';
}

In dot.move:1 1
In main:1 1

初始化的酷酷寫法+

初始化時給參數

struct dot{
    int x,y;
    dot():x(0),y(0){}
    dot(int a,int b):x(a),y(b){}
}

運算子重載

自己定義struct的

  • + - * / %

  • += -=...

  • 輸入輸出(>> <<)

假如你要重載...

a+b
a.operator+(b)

呼叫

假如你要重載...

a+=b
a.operator+=(b)

呼叫

假如你要重載...

a+b
a+=b
a+=3
a++

(a的型態)的operator+(b的型態)

(a的型態)的operator+=(b的型態)

(a的型態)的operator++()

(a的型態)的operator+=(int)

如何重載dot+dot?

//在struct定義的裡面
struct dot{
    dot operator+(dot other){
        //處理自己+other
    }
};
//在struct定義的外面
dot operator+(dot a,dot b){
    //處理a+b
}
型態 可不可以
int+int X
int+dot X
dot+int O
dot+dot O

如何重載輸入輸出?

先了解cin cout如何輸入輸出:

  • ostream的實體<<a
  • istream的實體>>a

ostream的實體<<a的回傳值是一個ostream的參考

cout<<a<<b    =>    (cout<<a)<<b

如何做到一長串輸入輸出?

這跟a=b=3一樣:

a=(b=3)

其實指定運算子都會回傳東西,所以可以

a= b+=5 之類的

(        )

int a=1,b=2;
a=b++;
//a=2,b=3
int a=1,b=2;
a=++b;
//a=3,b=3

考考你,這會輸出什麼?

int a=1,b=2;
(a=b)+=5;
cout<<a<<' '<<b<<'\n';

7 2

如何重載輸入輸出?

ostream& operator<<(ostream& s,dot a){
    s<<a.x<<' '<<a.y;
    return s;
}
istream& operator>>(istream& s,dot& a){
    s>>a.x>>a.y;
    return s;
}
dot a;
cin>>a;
cout<<a<<'\n';

不常用,底爸(debug)時才會自訂輸出,輸入也不必多此一舉

今天的總結

寫一個結串列

鏈結串列是什麼

記下每個元素的值外

再多記下一個元素是哪個

可以幹嘛?

1

13

8

5

3

2

1

a[7]={
}

普通的陣列要刪掉一個元素時:

可以幹嘛?

1

13

8

5

2

1

a[6]={
}

普通的陣列要刪掉一個元素時:

O(N)!!!!

可以幹嘛?

1

1

鏈結串列要刪掉一個元素時:

3

2

5

8

13

可以幹嘛?

1

1

2

5

8

13

鏈結串列要刪掉一個元素時:

可以幹嘛?

1

1

2

5

8

13

鏈結串列要刪掉一個元素時:

可以幹嘛?

1

1

2

5

8

13

鏈結串列要刪掉一個元素時:

插入元素

可以幹嘛?

1

1

鏈結串列要加一個元素時:

2

8

13

5

可以幹嘛?

1

1

2

8

13

5

3

鏈結串列要加一個元素時:

struct node{
    int val;
    node *next=NULL;
};

用重載運算子可以幹嘛?

struct node{
    int val;
    node *next=NULL;
    int& operator[](int step){
        if(step==0){
            return val;
        }
        if(next==NULL){
            cout<<"ERROR\n";
            return val;
        }
        return (*next)[step-1];
    }
};
node a,b,c;
a.next=&b;
b.next=&c;
a.val=1;
b.val=2;
c.val=3;
cout<<a[0]<<' '<<a[2]<<'\n';

如果你還嫌太麻煩的話

struct node{
    int val;
    node *next=NULL;
    node(int a):val(a){}
    int& operator[](int step){
        if(step==0){
            return val;
        }
        if(next==NULL){
            cout<<"ERROR\n";
            return val;
        }
        return (*next)[step-1];
    }
    node& operator+=(node& n){
        next=&n;
        return *this;
    }
};
node a(1),b(2),c(3);
a+=b+=c;
cout<<a[0]<<' '<<a[1]<<' '<<b[1]<<'\n';

例題

#include <iostream>
using namespace std;
struct node{
    int val;
    bool dead=0;
    node *next=NULL;
    node& operator+=(node& n){
        next=&n;
        return *this;
    }
};
int const N=1e6+10;
int n,m;
node d[N];
int main(){
    cin>>n>>m;
    for(int i=1;i<=n-1;++i){
        d[i].val=i;
        d[i]+=d[i+1];
    }
    d[n].val=n;
    int killer;
    for(int i=0;i<m;++i){
        cin>>killer;
        if(d[killer].dead||d[killer].next==NULL){
            cout<<"0u0 ...... ?\n";
            continue;
        }
        cout<<d[killer].next->val<<'\n';
        d[killer].next->dead=1;
        d[killer]+=*(d[killer].next->next);
    }
}

指標、參考、函數、遞迴、運算子重載、結構(暑假資讀)

By kennyfs

指標、參考、函數、遞迴、運算子重載、結構(暑假資讀)

沒過初選QAQ

  • 876