C++ Map 基础知识

2019/07/25 C++

C++ STL Map 的创建、删除、插入、更新、遍历

C++ 中map 容器提供一个键值对容器,map 与multimap 差别仅仅在于multimap允许一个键对应多个值。

map的实现是一颗红黑树,因此,map的内部键的数据都是排好序的,查找和删除、插入的效率都是lgN

map的本质其实就是映射,键值(key-value)一一对应。比如身份证号(key)和姓名(value)一一对应,map的定义格式:

std::map <key_type, value_type> 变量;

key 和value 可以是任意类型。

1、创建

#include   <map> 

int main(int argc, const char * argv[]) {
    
    map<int, int> m; //这里 定义为int ,后面插入数据时key 和value 只能是int 类型。
    printf("size:%lu\n",m.size());
    cout << "is empty:"<< m.empty() << endl;
}


log:

size:0
is empty:1

2、插入

1) map的变量名[key] = value;

  m[nums[i]] = i;// 如果键已经存在,则直接覆盖

2) 使用insert 方法

  //如果键已经存在,则插入失败
  m.insert(pair<int, int>(nums[i],i));
  或者
  m.insert(make_pair<int, int>(nums[i],i));
  
  
  int main(int argc, const char * argv[]) {
    
    map<int, int> m;
    
    printf("size:%lu\n",m.size());
    cout << "is empty:"<< m.empty() << endl;
    
    int nums[] = {1,2,3,4,5,6,7,8,9,10};
    map<int, int>::iterator it;
    for (int i = 0 ; i < 10; i++) {
        m.insert(pair<int, int>(nums[i],i));
//        m[nums[i]] = i;
     }
  }
  

顺便说一下pair

pair是将2个数据组合成一组数据,当需要这样的需求是就可以使用pair,如STL 中的map 就是key 和value 放在一起保存。另一个应用是,当一个函数需要返回2个数据的时候,可以选择pair。pair 的实现是一个结构体,主要的两个成员变量是first、second,因为是使用struct,不是class,所以可以直接使用pair 的成员变量。

  pair<int ,double> p1;
  
  
  p1.first = 1;
  p1.second = 2;
  
  cout<<p1.first<<' '<<p1.second<<endl;
  
  
//输出结果:1 2

pair<int, double> p1;
p1 = make_pair(1, 1.2);
 
cout << p1.first << p1.second << endl;
 
//output: 1 1.2


3、查找和修改

  map<int, int>::iterator iter1;
  iter1 = m.find(nums[i]);
  
  iter1->second = 1;
  
  或者
  
  int value = m[nums[i]];
  m[nums[i]] = 1;
  
  
  int main(int argc, const char * argv[]) {
    
    map<int, int> m;
    
    printf("size:%lu\n",m.size());
    cout << "is empty:"<< m.empty() << endl;
    
    int nums[] = {1,2,3,4,5,6,7,8,9,10};
    map<int, int>::iterator it;
    for (int i = 0 ; i < 10; i++) {
        m.insert(pair<int, int>(nums[i],i));
//        m[nums[i]] = i;
     }
    
     map<int, int>::iterator iter1;
     iter1 = m.find(1);
    
     int num = m[2];
     printf("%d\n",num);
   }
   
   log:
   
   
   size:0
   is empty:1
	1

4、删除

  for(int i = 0;i < nums.size();i++) {
      iter = m.find(nums[i]);
      if(iter != m.end()) {
            m.erase(iter);
	   }      
    }

5、遍历

  //正向遍历
   map<int, int>::iterator iter3 = m.begin();
    while (iter3 != m.end()) { 
        printf("%d ",iter3->second);
        iter3++;
    }
    
   //反向遍历
   map<int, int>::reverse_iterator iter2 = m.rbegin();
    while (iter2 != m.rend()) {
        printf("%d ",iter2->second);
        iter2++;
    }
    
    //边遍历边删除
    map<char, int>::iterator iter;
    
    iter = mMap.begin();
    while (iter != mMap.end()) {
        if (iter->second > 1) {
           iter = mMap.erase(iter);//返回下一个元素指针来获取最新的iterator
        }else {
          iter++;
        }
        
    } 

这是另一个算法题

  int singleChar(const char* str) {
    if (str == NULL) {
        return -1;
    }
    
    map<char, int> mMap;
    for (int i = 0; i < strlen(str); i++) {
        map<char, int>::iterator iter;
        iter = mMap.find(str[i]);
        int count = 0;
        if (iter != mMap.end()) {
            count = iter->second;
        }
        count++;
        mMap[str[i]] = count;
    }
    map<char, int>::iterator iter;
    
    iter = mMap.begin();
    while (iter != mMap.end()) {
        if (iter->second > 1) {
           iter = mMap.erase(iter);
        }else {
          iter++;
        }
        
    }
    
    for (int i = 0; i < strlen(str); i++) {
        map<char, int>::iterator iter;
        iter = mMap.find(str[i]);
        if (iter != mMap.end()) {
            return i;
        }
    }
    return -1;
}
//目前只想到这个方法

6、排序

map 的排序默认是根据key 从小到大排序。

7、其他方法

  m.size();//返回元素数目
  m.empty();//判断是否为空
  m.clear();//清空所有元素
  m.count(nums[i]);//判断某个元素是否存在,由于map 中key 不能重复,因此,返回值要么是0,要么是 1
  

最后实际应用:

leetcode 136 题

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

class Solution {
public:
	int singleNumber(vector<int>& nums) {
        map<int,int> m;
        map<int,int>::iterator iter;
        for(int i = 0;i < nums.size();i++) {
            iter = m.find(nums[i]);
            if(iter != m.end()) {
                m.erase(iter);
            }else {
                m.insert(pair<int,int>(nums[i],i));
            }
            
        }
        iter = m.begin();
        int ret = 0;
        
        ret = iter->first;
        return ret;
    }
};


int main(int argc, const char * argv[]) {
    
    map<int, int> m;
    
    int nums[] = {1,2,3,4,5,6,7,8,9,10};
    map<int, int>::iterator it;
    for (int i = 0 ; i < 10; i++) {
        m.insert(pair<int, int>(nums[i],i));
    }

    vector<int> n = {2,2,1};
    Solution *s1 = new Solution();
    int ret1 = s1->singleNumber(n);
    printf("%d\n",ret1);
    printf("\n");
    
    return 0;
}

还是有高手厉害:

异或运算满足以下定律:(离散数学有详解)

交换律: a ^ b ^ c <=> a ^ c ^ b

任何数与0异或 0 ^ n = n

相同的数异或为0 n ^ n = 0

0^0 = 0,

1^0 = 1,

0^1 = 1,

1^1 = 0

根据三条定律,用0与数组中所有元素分别异或,最后会得到没有相同的那个数。

int singleNumber(vector<int>& nums) {
    
    int result = 0;
    for(int e : nums)
    {
        result ^=e;
    }
    return result;
}

复习一下:

按位与运算(&)

运算规则:

0&0=0;  

0&1=0;   

1&0=0;   
 
1&1=1;

按位或运算(

运算规则:

0|0=0;  

0|1=1;  

1|0=1;   

1|1=1;

异或运算符(^)

运算规则:

0^0=0;  

0^1=1;  

1^0=1;   

1^1=0;

取反运算符(~)

运算规则:

~1=0;  

~0=1;

使一个数的最低位为零,可以表示为:a&~1。

~1的值为1111111111111110,再按“与”运算,最低位一定为0。因为“~”运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。

复合赋值运算符

位运算符与赋值运算符结合,组成新的复合赋值运算符,它们是:

&=   例:a &=b       相当于a=a& b

|=   例:a |=b       相当于a=a |b

>>=  例:a >>=b      相当于a=a>> b

<<= 例:a<<=b      相当于a=a<< b

^=   例:a ^= b      相当于a=a^ b

参考:

[1]:https://blog.csdn.net/xiaopihaierletian/article/details/78162863

Search

    Post Directory