数据结构和算法库是程序编写最基础的库之一。字符串是人类语言的记录,是计算机处理的主要对象,因此字符串数据和算法是重中之重。
C和C++ 由于C语言不提供数据结构和算法库,这里专指C++的STL stand template library。
数据结构 vector 是一个动态数组,随机访问时间O(1),可以快速地在末尾插入或删除元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <vector> #include <iostream> int main () { std::vector<int > vec = {1 , 2 , 3 , 4 , 5 }; vec.push_back (6 ); for (int val : vec) { std::cout << val << " " ; } std::cout << std::endl; std::cout << vec[2 ] << std::endl; return 0 ; }
list 是双向链表,能高效插入或删除元素,但不提供随机访问接口
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <list> #include <iostream> int main () { std::list<int > lst = {1 , 2 , 3 , 4 , 5 }; lst.push_back (6 ); lst.push_front (0 ); for (int val : lst) { std::cout << val << " " ; } return 0 ; }
deque 是双端队列,头部和尾部可以进行高效的插入和删除操作。deque也提供随机访问接口
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <deque> #include <iostream> int main () { std::deque<int > deq = {1 , 2 , 3 , 4 , 5 }; deq.push_back (6 ); deq.push_front (0 ); for (int val : deq) { std::cout << val << " " ; } return 0 ; }
set 是一个集合,自动排序且不允许重复元素。unordered_set 是一个不排序的集合,元素根据哈希值存储,允许快速查找和插入。
map 是一个映射,存储键值对(key-value),并根据键进行排序。unordered_map 是一个无序映射,存储键值对,使用哈希表进行存储。
1 2 3 4 5 6 7 8 9 10 11 #include <unordered_map> #include <iostream> int main () { std::unordered_map<int , std::string> um = {{1 , "one" }, {2 , "two" }, {3 , "three" }}; for (const auto & pair : um) { std::cout << pair.first << ": " << pair.second << std::endl; } return 0 ; }
迭代器,C++ STL使用迭代器来标识容器中的元素,以及实现容器中元素的访问。迭代器支持*
和->
运算符来解引用或访问元素成员变量
随机访问迭代器 (Random Access Iterator),类似指针,通过加减算数提供随机访问。对应容器std::vector、std::deque、std::array
。支持随机访问迭代器作为参数的算法函数,一般也支持指针作为参数。
双向迭代器(Bidirectional Iterator),允许在容器中向前和向后移动。对应容器std::list、std::set、std::map。
迭代器是使用继承结构组织的,随机访问迭代器也是一种双向迭代器
算法 std::sort:对容器中的元素进行排序,默认升序。需要传入随机访问迭代器作为参数
std::sort是不稳定的,std::stable_sort提供稳定排序,即保证相同元素的相对顺序
1 2 3 4 5 std::vector<int > vec = {4 , 2 , 9 , 1 }; std::sort (vec.begin (), vec.end ()); std::sort (vec.begin (), vec.end (), [](int a, int b) { return a > b; });
std::find:查找容器中的某个元素,返回第一个匹配元素的迭代器。传入双向迭代器即可
1 2 3 4 auto it = std::find (vec.begin (), vec.end (), 2 );if (it != vec.end ()) { }
std::lower_bound,传入排序好的数组,返回第一个大于或等于指定元素的迭代器。 std::upper_bound:返回指向已排序数组中第一个大于指定元素的迭代器。
需要传入随机访问迭代器
1 2 auto it = std::lower_bound (vec.begin (), vec.end (), 3 );auto it = std::upper_bound (vec.begin (), vec.end (), 3 );
std::reverse:反转容器中的元素顺序。
1 std::reverse (vec.begin (), vec.end ());
std::fill:将容器中的所有元素设置为指定值。
1 std::fill (vec.begin (), vec.end (), 0 );
std::transform:对容器中的每个元素应用指定的操作。
1 std::transform (vec.begin (), vec.end (), vec.begin (), [](int x) { return x * 2 ; });
std::copy:将容器中的元素复制到另一个容器。std::swap:交换两个容器的内容。std::move:移动容器的元素,避免复制。
1 2 3 std::copy (vec, dest); std::swap (vec, dest); std::vector<int > new_vec = std::move (vec);
std::count:统计容器中某个元素出现的次数。std::count_if:统计符合特定条件的元素的数量。
1 2 int count = std::count (vec.begin (), vec.end (), 2 );int count = std::count_if (vec.begin (), vec.end (), [](int x) { return x % 2 == 0 ; });
std::accumulate:对容器中的元素进行累加。
1 int sum = std::accumulate (vec.begin (), vec.end (), 0 );
std::min_element:返回容器中最小元素的迭代器。std::max_element:返回容器中最大元素的迭代器。
1 2 auto min_it = std::min_element (vec.begin (), vec.end ());auto max_it = std::max_element (vec.begin (), vec.end ());
字符串处理函数 由于C++兼容C语言,因此支持C风格和C++风格的字符串处理算法。C风格字符串函数参数是指针,C++风格是随机访问迭代器。
C风格的字符串处理函数, 在C++中,执行#include <cstring>
,C语言的字符串处理函数作为std namespace的全局函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 char src[] = "Hello, World!" ;char dest[20 ];std ::strcpy (dest, src);std ::strncpy (dest, src, 5 );std ::strcat (str1, str2); std ::strncat (str1, str2, 5 );std ::strcmp (str1, str2)char * ptr = std ::strchr (str, 'o' );const char * str = "Hello, World!" ;const char * sub = "World" ;char * ptr = std ::strstr (str, sub);char str[20 ];std ::memset (str, '*' , 5 ); memcpy (dest, src, n) 从src拷贝n个字节到destchar src[] = "Hello" ;char dest[10 ];std ::memcpy (dest, src, 6 );
C++ string是动态字符串,如果希望字符串不可变,可以使用const修饰。string支持的函数如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 std::string str = "Hello" ; str.append (", World!" ); str.push_back ('!' ); str.insert (5 , ", World" ); str.erase (5 , 7 ); str.replace (7 , 5 , "C++" ); str.resize (5 ); size_t pos = str.find ("World" );size_t pos = str.find_first_of ("o" );std::string sub = str.substr (7 , 5 ); std::string str1 = "Hello" ; std::string str2 = "World" ; int result = str1. compare (str2);int num = std::stoi (str);std::string str = std::to_string (num); const char * cstr = str.c_str ();
JAVA 数据结构 ArrayList,等于C++的vector,动态数组。JAVA的List是一个接口,包括动态数组和链表。
1 2 3 4 5 6 7 import java.util.ArrayList;ArrayList<String> list = new ArrayList <>(); list.add("Apple" ); list.add("Banana" ); list.add("Cherry" ); System.out.println(list);
LinkedList,双向链表,相当于C++的list
1 2 3 import java.util.LinkedList;LinkedList<String> list = new LinkedList <>();
HashSet, 基于哈希表实现,不保证元素的顺序。
LinkedHashSet, 基于哈希表和链表实现,能够保证元素的插入顺序。
TreeSet, 基于红黑树实现,元素按自然顺序或自定义顺序排列。
HashMap,基于哈希表实现,允许 null 键和值。
LinkedHashMap,基于哈希表和链表实现,保持插入顺序(或访问顺序)
TreeMap,基于红黑树实现,键按自然顺序或自定义顺序排序。
PriorityQueue,基于堆实现的队列,元素按优先级顺序排列
LinkedList 也可以实现 Queue 接口,支持双端队列操作。
1 2 3 4 5 6 7 8 9 import java.util.LinkedList;import java.util.Queue;Queue<String> queue = new LinkedList <>(); queue.add("Apple" ); queue.add("Banana" ); queue.add("Cherry" ); System.out.println(queue.poll()); System.out.println(queue);
算法 Arrays.sort() 用于对数组进行排序,数组是基本类型数组int[] arr
,或对象数组new Integer[10]
Collections.sort() 用于对集合(如 List)进行排序
1 2 3 4 5 6 7 8 9 import java.util.*;public class SortExample { public static void main (String[] args) { List<Integer> list = new ArrayList <>(Arrays.asList(5 , 2 , 8 , 1 , 3 )); Collections.sort(list); System.out.println(list); } }
Arrays.binarySearch(),数组查找;Collections.binarySearch()在容器查找
1 2 3 4 5 6 7 8 9 import java.util.*;public class BinarySearchExample { public static void main (String[] args) { List<Integer> list = new ArrayList <>(Arrays.asList(1 , 2 , 3 , 5 , 8 )); int index = Collections.binarySearch(list, 5 ); System.out.println(index); } }
Arrays.fill(), Collections.fill() 填充
1 2 3 4 5 public static void main (String[] args) { List<Integer> list = new ArrayList <>(Arrays.asList(1 , 2 , 3 , 4 , 5 )); Collections.fill(list, 10 ); System.out.println(list); }
Collections.shuffle() 用于随机打乱 List 中元素的顺序
1 2 3 4 5 public static void main (String[] args) { List<Integer> list = new ArrayList <>(Arrays.asList(1 , 2 , 3 , 4 , 5 )); Collections.shuffle(list); System.out.println(list); }
字符串处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 String str1 = "Hello" ; String str2 = new String ("Hello" ); int length = str.length();s.charAt(0 ) String sub = s.substring(0 , 5 ); String result = s1 + " " + s2; StringBuilder sb = new StringBuilder (); sb.append("Hello" ); sb.append(" " ); String trimmed = s.trim(); String s = "Hello, World!" ;int index = s.indexOf("World" ); String upper = s.toUpperCase(); String formatted = String.format("My name is %s and I am %d years old." , name, age); String s = "apple,banana,cherry" ;String[] fruits = s.split("," );
JAVA的String是不可变的(String类被声明成final),String 字符串每次拼接都会创建新的对象。
StringBuilder 和 StringBuffer 是用于可变字符串的类。StringBuilder只能用于单线程环境,StringBuffer则是线程安全的。
修改字符串某位置的字符
1 2 3 4 5 6 7 8 9 10 11 StringBuilder sb = new StringBuilder ("Hello" );sb.setCharAt(1 , 'a' ); System.out.println(sb.toString()); String str = "Hello" ;char [] charArray = str.toCharArray();charArray[1 ] = 'a' ; String modifiedStr = new String (charArray);System.out.println(modifiedStr);
Go 数据结构 golang 用语言关键字提供了容器,不必额外导入包
数组, 固定大小的同一类型的元素。Go 数组的大小在声明时确定并且内容不可更改。
1 2 3 4 var arr [5 ]int arr[0 ] = 1 arr[1 ] = 2 fmt.Println(arr)
切片slice,动态数组。[]byte表示动态字符串。
1 2 3 4 5 slice := []int {1 , 2 , 3 , 4 , 5 } slice = append (slice, 6 )
map,哈希表实现的容器,允许通过键来快速查找和存储值,无序
1 2 3 4 5 6 myMap := map [string ]int { "apple" : 5 , "banana" : 3 , } myMap["orange" ] = 7
链表,使用container/list
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport ( "container/list" "fmt" ) func main () { l := list.New() l.PushBack(1 ) l.PushFront(0 ) for e := l.Front(); e != nil ; e = e.Next() { fmt.Println(e.Value) } }
channel,channel可认为是一个线程安全的协程间消息/任务队列
算法 排序,import “sort”标准库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package mainimport ( "sort" ) func main () { numbers := []int {5 , 2 , 9 , 1 , 5 , 6 } sort.Ints(numbers) } type Person struct { Name string Age int } type ByAge []Personfunc (a ByAge) Len() int { return len (a) }func (a ByAge) Less(i, j int ) bool { return a[i].Age < a[j].Age }func (a ByAge) Swap(i, j int ) { a[i], a[j] = a[j], a[i] }func main () { people := []Person{ {"Alice" , 25 }, {"Bob" , 20 }, {"Charlie" , 30 }, } sort.Sort(ByAge(people)) }
字符串处理函数 Go中,string是不可变的 UTF-8 编码字符串, 无法原地修改字符串(编译失败)。[]byte是字符序列切片,可原地修改字符串。
1 2 3 4 5 s := "Hello, World!" b := []byte (s) b := []byte {72 , 101 , 108 , 108 , 111 } s := string (b)
字符串处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 str := "Hello" len (str) str[1 ] str1 := "Hello" str2 := "World" result := str1 + ", " + str2 strings.Index(str, "World" ) strings.Contains(str, "World" ) strings.Split(str, "," ) strings.TrimSpace(str) strings.ToUpper(str) strings.ToLower(str) strings.HasPrefix(str, "go" ) strings.HasSuffix(str, ".org" ) result := fmt.Sprintf("Name: %s, Age: %d" , name, age) strconv.Atoi(numStr)
Python 数据结构 列表,动态数组。和go一样,python列表支持切片。
1 2 3 my_list = [1 , 2 , 3 , 4 , 5 ] my_list[0 ] = 10 my_list.append(6 )
元组Tuple,不可变,只可被访问
1 2 my_tuple = (1 , 2 , 3 ) a, b, c = my_tuple
字典dict, 无序(Python 3.7 及以上版本中插入有序)、键值对、键唯一
1 2 my_dict = {"name" : "Alice" , "age" : 25 } my_dict["age" ] = 30
集合set,无序、不重复。
1 2 my_set = {1 , 2 , 3 , 4 , 5 } my_set.add(6 )
有序字典, 保留键值对插入顺序
1 2 3 4 5 from collections import OrderedDictordered_dict = OrderedDict() ordered_dict["a" ] = 1 ordered_dict["b" ] = 2
算法 排序
sorted(iterable, key=None, reverse=False), 返回一个新的排序列表,不改变原列表。
1 2 3 4 5 6 nums = [5 , 2 , 9 , 1 ] print (sorted (nums)) print (sorted (nums, reverse=True )) words = ["apple" , "banana" , "cherry" ] print (sorted (words, key=len ))
list.sort(), 原地排序,直接修改列表
1 2 nums = [5 , 2 , 9 , 1 ] nums.sort()
字典排序
1 2 3 my_dict = {"b" : 2 , "a" : 1 , "c" : 3 } sorted_keys = sorted (my_dict) sorted_items = sorted (my_dict.items(), key=lambda item: item[1 ])
字符串处理 1 2 3 4 5 6 7 8 9 10 11 12 13 s = "Hello" len (s)s[1 ] s[-1 ] s[1 :3 ] s + " " print (f"My name is {name} and I am {age} years old." ) s.upper(),s.lower() s.strip() s.find("World" ) s.replace("World" , "Python" ) s.split("," )
对于python2, 还有个编码的问题。处理办法是,对于u开头的字符串或者unicode类型的字符串,都使用encode(“utf-8”) 编码为str类型后,再使用。
1 2 3 4 5 6 7 unicode_str = u"中文" byte_str = "byte" print (type (unicode_str)) print (type (byte_str)) print (type (unicode_str.encode("utf-8" )))