
* 문제
Digitville 마을에는 0부터 n-1까지의 숫자가 각각 한 번씩만 등장해야 하는 리스트 nums가 있었다.
하지만 두 개의 장난꾸러기 숫자가 한 번 더 숨어 들어가 리스트가 원래 길이보다 길어져 버렸다.
당신은 이 마을의 탐정으로서, 두 번 등장한 숫자 두 개를 찾아내야 한다.
순서는 상관 없으며, 두 숫자를 배열 형태로 반환하면 된다.
* 예시 1
입력: nums = [0,1,1,0]
출력: [0,1]
설명: 0과 1이 각각 두 번 등장했다.
* 예시 2
입력: nums = [0,3,2,1,3,2]
출력: [2,3]
설명: 2와 3이 두 번씩 등장했다.
* 예시 3
입력: nums = [7,1,5,4,3,4,6,0,9,5,8,2]
출력: [4,5]
설명: 4와 5가 두 번 등장했다.
* 조건
2 <= n <= 100
nums.length == n + 2
0 <= nums[i] < n
리스트에는 정확히 두 숫자만 중복된다.
일단 2숫자만 중복이 되므로 리스트에서 중복되는 숫자 2개만 찾으면 코드가 끝나게 만들면 될 것 같다.
class Solution {
List<int> getSneakyNumbers(List<int> nums) {
nums.sort();
List<int> sneakyNum = [];
for (var i = 0; i < nums.length - 1; i++) {
if (nums[i] == nums[i + 1]) {
sneakyNum.add(nums[i]);
}
}
if (sneakyNum.length != 2) {
sneakyNum.add(nums[nums.length - 1]);
}
return sneakyNum;
}
}
숫자들을 오름차순으로 정렬해서 현재 nums[i]과 바로 뒤에 있는 nums[i]가 같다면 sneakyNum에 추가해서 sneakyNum을 return하게 만들었는데 런타임이 6ms가 걸렸다. 다른 사람 통계에 1ms도 있길래 시간을 줄일 방법이 있나 생각해봤다.
새로 map으로 저장해서 똑같은 key가 있다면 value 값음 +1 하고 value 값이 2인 key만 내보내는 방법을 생각했다.
class Solution {
List<int> getSneakyNumbers(List<int> nums) {
Map<int, int> sneakyNum = {};
List<int> sneakyNums = [];
for (var i = 0; i < nums.length; i++) {
sneakyNum.update(nums[i], (value) => (value) + 1, ifAbsent: () => 1);
}
for (var n in sneakyNum.entries) {
if (n.value == 2) {
sneakyNums.add(n.key);
}
}
return sneakyNums;
}
}
update를 사용해서 만들었는데 이게 런타임이 더 걸린다는 사실~ 그만 생각하기로 했다..
* 문제
문자열 jewels는 보석의 종류를 나타내고, 문자열 stones는 당신이 가진 돌들을 나타낸다.
stones의 각 문자(문자 하나)는 당신이 가진 돌의 종류이다. 당신이 가진 돌들 중에서 보석에 해당하는 것이 몇 개인지 알고 싶다.
문자는 대소문자를 구분하므로 "a"는 "A"와 다른 종류로 취급된다.
* 예시 1:
입력: jewels = "aA", stones = "aAAbbbb"
출력: 3
* 예시 2: 입력: jewels = "z", stones = "ZZ"
출력: 0
* 제한 조건:
1 <= jewels.length, stones.length <= 50
jewels와 stones는 영어 알파벳만으로 이루어져 있다.
jewels의 모든 문자는 중복되지 않고 유일하다.
리스트로 되어있을 때는 뭔가 비교하기 쉬울 것 같은데 문자열로 되어있어서 비교하기 어려웠다.. 그래서 찾아보니 split()을 사용하면 문자열을 나눠서 List에 넣을 수 있다는 걸 보고 split에 대해 공부한 후 다시 풀었다.
class Solution {
int numJewelsInStones(String jewels, String stones) {
int count = 0;
List<String> jewelsList = jewels.split('');
List<String> stonesList = stones.split('');
for (var i = 0; i < jewelsList.length; i++) {
if (stonesList.contains(jewelsList[i])) {
count++;
}
}
return count;
}
}
처음에 이렇게 코드를 짰는데 생각해보니 이 방법은 jewelsList.length 길이만 반복해서 다른 반목문을 사용하는 게 맞다.. ㅋㅁㅋ..
class Solution {
int numJewelsInStones(String jewels, String stones) {
int count = 0;
List<String> jewelsList = jewels.split('');
List<String> stonesList = stones.split('');
for (var s in stonesList) {
if (jewelsList.contains(s)) {
count++;
}
}
return count;
}
}
stonesList가 더 값이 기니 stonesList를 돌면서 jewelsList에 값이 있는지 확인하는 게 더 나은 방법이담 이렇게 제출하니 성공~
화면 위에 떠 있는 주요 액션 버튼
앱에서 가장 핵심 동작에 사용
보통 화면 한 곳에 고정되며 스크롤해도 그대로 남음
Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
);
Scaffold 안에 위치
FloatingActionButton.extended(
onPressed: () {},
icon: const Icon(Icons.edit),
label: const Text('작성하기'),
);
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
| 옵션 | 위치 |
|---|---|
endFloat | 오른쪽 아래 기본 위치 |
centerFloat | 아래 중앙 |
startFloat | 왼쪽 아래 |
centerDocked | BottomBar 중앙에 파묻힌 디자인 |
endDocked | BottomBar 오른쪽 결합 |
FloatingActionButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
backgroundColor: Colors.red,
elevation: 4,
child: const Icon(Icons.add, size: 28),
)
| 요소 | 목적 |
|---|---|
| Bottom Navigation Icon | 화면 이동 |
| Floating Action Button | 핵심 동작 수행 |
길이를 가지고 있고 인덱스로 접근 가능한 컬렉션
이 클래스를 상속한 리스트들은 두 종류로 나뉜다:
길이를 변경할 수 없음
요소 변경은 가능하지만 추가/삭제 불가
final fixedLengthList = List<int>.filled(5, 0);
fixedLengthList[0] = 87; // 가능
fixedLengthList.length = 0; // 오류
fixedLengthList.add(499); // 오류
길이 변경 가능
add, addAll, insert, remove 등 전체 API 사용 가능
final growableList = <String>['A', 'B'];
growableList.add('X');
growableList.remove('A');
growableList.insert(1, 'New');
| 기능 | 메서드 |
|---|---|
| 값 추가 | add, addAll, insert, insertAll |
| 값 변경 | []=, replaceRange, fillRange, setRange |
| 값 삭제 | remove, removeAt, removeLast, removeRange, removeWhere |
| 값 위치 확인 | indexOf, lastIndexOf |
| 정렬 | sort |
| 섞기 | shuffle |
| 조건에 맞는 첫 요소 찾기 | firstWhere |
| 마지막 요소 찾기 | lastWhere, singleWhere |
final lst = [1,2];
lst.add(3); // [1,2,3]
lst.addAll([4,5]); // [1,2,3,4,5]
lst.insert(1, 9); // [1,9,2,3,4,5]
고정 리스트는 이메서드 사용 불가 → UnsupportedError
final lst = [1,2,3,2];
lst.remove(2); // [1,3,2]
var x = lst.removeAt(1); // x=3, [1,2]
lst.removeLast(); // [1]
반복 중에 remove로 길이 변경하면 ConcurrentModificationError 발생 가능
[1,2,3,2].indexOf(2); // 1
[1,2,3].contains(4); // false
var a = [0,1,2,3,4];
var s = a.sublist(1,4); // [1,2,3]
a.setRange(1,3, [9,9]); // [0,9,9,3,4]
a.fillRange(0,2, 5); // [5,5,9,3,4]
a.replaceRange(2,4, [7,8,9]); // [5,5,7,8,9,4]
setRange는 복사(source와 destination이 겹칠 때 내부적으로 안전 처리하지만 구현에 따라 주의)
var l = [3,1,2];
l.sort(); // [1,2,3]
// 커스텀 비교:
l.sort((a,b) => b.compareTo(a)); // 내림차순
sort 도중 리스트를 수정하면 ConcurrentModificationError 발생 가능
비교 함수는 일관된 비교(반사성·대칭성·추이성)을 지켜야 함
split()은 기준을 만나면 그 지점에서 잘라서 리스트로 돌려줌
'Hello world'.split(' ');
공백을 기준으로 자름 → 결과: ['Hello', 'world']
'a,b,c'.split(',');
, 기준으로 자름
결과: ['a', 'b', 'c']
'1,,2'.split(',');
,가 연속으로 나오면 그 사이에 내용이 없으니까 빈 문자열 포함
결과: ['1', '', '2']
'a b c'.split(RegExp(r'\s+'));
연속 공백 전부를 기준으로 한 번에 자름
결과: ['a', 'b', 'c']
'hello'.split('x');
x가 없음 → 안 잘림
결과: ['hello'] (그냥 원본 하나)
'Pub'.split('');
글자 하나씩 다 쪼갬
결과: ['P', 'u', 'b']
유니코드 기준이 아니라 UTF-16 코드 단위로 쪼갬
그래서 이모지 같은 건 반쪽으로 쪼개질 수 있음
''.split('');
결과: []
비어있어서 쪼갤 게 없음
''.split('a');
결과: []
매치도 없고 쪼갤 것도 없음
파일 시스템의 파일 경로를 참조하는 객체
파일 읽기/쓰기/삭제/확인 등을 수행
부모 디렉터리는 parent로 접근
var myFile = File('file.txt');
var text = await File('file.txt').readAsString();
print(text);
var lines = File('file.txt').openRead()
.transform(utf8.decoder)
.transform(LineSplitter());
await for (var line in lines) {
print(line);
}
await File('file.txt').writeAsString('내용');
var sink = File('file.txt').openWrite();
sink.write('로그 추가\n');
await sink.flush();
await sink.close(); // 반드시 닫기
오늘부터 개인과제를 시작했는데 아직 객체와 클래스 부분이 헷갈린다.. 뭔가 익숙한 느낌인 것 같으면서 알쏭달쏭하달까.. 다시 내가 쓴 벨로그를 보면서 복습과 동시에 과제를 하고 있다. 이 부분은 앞으로 자주 쓰일 것 같아서 익히고 넘어가는 게 좋을 것 같담