>알고리즘 문제
1. 문제
문제 설명
이중 우선순위 큐는 다음 연산을 할 수 있는 자료구조를 말합니다.
명령어수신 탑(높이)I 숫자 | 큐에 주어진 숫자를 삽입합니다. |
D 1 | 큐에서 최댓값을 삭제합니다. |
D -1 | 큐에서 최솟값을 삭제합니다. |
이중 우선순위 큐가 할 연산 operations가 매개변수로 주어질 때, 모든 연산을 처리한 후 큐가 비어있으면 [0,0] 비어있지 않으면 [최댓값, 최솟값]을 return 하도록 solution 함수를 구현해주세요.
제한사항- operations는 길이가 1 이상 1,000,000 이하인 문자열 배열입니다.
- operations의 원소는 큐가 수행할 연산을 나타냅니다.
- 원소는 “명령어 데이터” 형식으로 주어집니다.- 최댓값/최솟값을 삭제하는 연산에서 최댓값/최솟값이 둘 이상인 경우, 하나만 삭제합니다.
- 빈 큐에 데이터를 삭제하라는 연산이 주어질 경우, 해당 연산은 무시합니다.
2. 솔루션
import java.util.*
class Solution {
fun solution(operations: Array<String>): IntArray {
var answer = intArrayOf()
var size=0
val upq=PriorityQueue<Node>(compareBy{-it.n})
val dpq=PriorityQueue<Node>(compareBy{it.n})
for(i in operations){
if(i == "D 1"){
if(size>0){
getNonNull(upq)
size--
}
}else if(i =="D -1"){
if(size>0){
getNonNull(dpq)
size--
}
}else{
i.split(" ")[1].toInt().let{
val node=Node(it)
upq.offer(node)
dpq.offer(node)
size++
}
}
}
if(size==1){
val tmp = getNonNull(upq)!!.n
return intArrayOf(tmp,tmp)
}else if(size>1){
answer+=getNonNull(upq)!!.n
answer+=getNonNull(dpq)!!.n
}
else{
return intArrayOf(0,0)
}
return answer
}
fun getNonNull(pq:PriorityQueue<Node>):Node?{
var tmp:Node? =null
while(true){
tmp = pq.poll()
if(tmp.check){
tmp.check=false
break
}
}
return tmp
}
}
data class Node(val n:Int, var check:Boolean=true)
-우선순위큐를 정순과 역순으로 하나씩 생성하고 사이즈를 재는 변수 하나 선언
-참조를 이용하기위해 데이터 클래스를 하나 생성
-노드추가는 둘 다 , 노드 제거는 한쪽에서만 진행하여 제거된 노드에 대해서는 check를 false로 바꾸어 반대 큐에서도 알 수 있게 함.
-위 조건을 지키며 operation을 진행하여 최종 큐를 얻어 결과를 도출한다.
>심화 주차 강의
1. Retrofit
1)서버와 클라이언트
<1>기본 개념
-서버:데이터나 리소스를 제공하는 시스템. 요청이 오면 그에 맞는 응답 전송
-클라이언트: 사용자를 대표해 서버에 정보나 서비스를 요청하는 시스템
=>웹 브라우저, 앱등이 있음
- 3-Tier 아키텍쳐: 클라이언트 - 서버 - 데이터베이스 순으로 연결되어있는 형태
=>클라이언트는 리소스를 사용, 서버는 리소스를 전달, 데이터베이스는 리소스를 저장
<2>서버 - 클라이언트 통신: 사용 프로토콜, 용도 , 성능 요구사항등에 따라 선택
-HTTP/HTTPS: 웹 기반 앱에서 주로 사용. REST API나 SOAP같은 웹 서비스 통식 방식에서 기반
-WebSockets: 실시간 양방향 통신(채팅, 실시간 게임등)
-Socket(TCP/UDP): TCP나 UDP프로토콜을 사용해 데이터 전송. 지속적 연결 유지하며 양방향 통신
-FTP(File Transfer Protocol): 파일 전송에 특화. 클라이언트와 서버같의 파일전송에 사용
-SOAP(Simple Object Access Protocol): XML기반 메시징 프로토콜
<3>프로토콜(Protocol): 통신 규약. 요청과 응답에 대한 약속
<4>웹 어플리케이션 프로토콜: HTTP
-클라이언트와 서버가 HTTP프로토콜을 이용해 통신을 하고 이때 주고받는 메시지는 HTTP메시지라 부른다.
<5>API(Application Programming Interface)
-두 구성 요소(앱)간 서로 통신할 수 있게 해주는 메커니즘.
-정의 및 프로토콜의 집합으로 구성됨.
=>통신에 사용되는 언어나 메시지 형식을 의미. 즉 일종의 매개체.
=>즉, API는 두 어플리케이션간(주로 서버와 클라이언트)의 서비스 계약(=서로 통신하는 방법에 대한 정의)
2)REST API(Representational State Transfer)
@ Representational:표현의란 뜻으로 여기서는 리소스가 클라이언트에게 전달된때의 표형 형식을 의미.
=>표현 형식으로는 JSON, XML등이 있다.
@REST의 의미: 클라이언트가 서버 데이터에 액세스하는데 사용할 수 있는 함수집합인 GET, PUT, DELETE등을 의미
<1>구조
-Client 와 Server가 있고 요청은 HTTP,URL로 이루어지며 응답은 JSON, XML등으로 이우어 진다.
-웹의 장점을 활용할 수 있는 아키텍처 스타일
-WWW가 REST아키텍처 기반으로 구성되어 있음
<2>핵심 원칙
[1]자원 식별: 각 리소스는 고유 URI로 식별됨
[2]메시지의 상태를 통한 표현: 리소스는 JSON, XML등의 형식으로 표현
!![3]상태가 없는(stateless)통신: 각 요청은 서버에서 필요한 모든 정보를 포함해야 함. 즉, 서버는 클라이언트의 데이터를 저장하지 않음 => 무상태
=>이를 통해 서버는 각 요청을 개별적으로 처리 가능
[4]클라이언트 - 서버 구조: 사용자 인터페이스와 데이터 저장소가 분리되어 독립성이 높아짐
[5]캐시 처리 가능: 응답 데이터 캐싱이 가능한지 여부를 명시해 성능 향상
[6]계층화된 시스템: 서버와 클라이언트 사이에 다양한 계층 (보안, 로드 밸런싱등)이 존재하 수 있음
<3>REST와 HTTP
-REST는 주로 HTTP프로토콜 위에서 구현\
-주요 HTTP메서드
[1]GET: 리소스 조회
[2]POST: 리소스 생성
[3]PUT: 리소스 수정
[4]:DELETE: 리소스 삭제
<4>RESTful API: 위의 REST원칙을 지킨 API
3)JSON: 데이터 저장, 전송시 사용되는 경량의 DATA교환 형식
<1>데이터 구조: 각 데이터는 NAME -VALUE 형식으로 이루어짐
-예시: { "이름1": "데이터1" , "이름2": "데이터2", ...}
=>NAME은 String, VALUE엔 다양한 primitive type의 값과 null이 올 수 있음
<2>JSON배열
-대괄호로 둘러쌓아 표현
-여러 JSON데이터를 포함할 수있음
-예시
"dog":[
{"name1":"value1"},
{"name2":"value2"}
]
-위와 같이 VALUE에 배열이 들어갈 수 있다.
4)GSON
<1>GSON이란: JSON을 직렬화(JSON을 프로그래밍 언어 객체로 변환), 역직렬화(객체를 JSON으로 변환)하기를 간단하게 해주는 라이브러리
<2>사용이유
-코드 간결성: JSON과 객체간 변환작업이 간단함
-성능 효율성: 직렬화 및 역직렬화 작업을 빠르게 수행하여 대규모나 복잡한 데이터 처리에도 효과적
<3>사용법
[1]직렬화(객체 -> JSON)
val gson = Gson()
val jsonString = gson.toJson(someObject)
[2]역직렬화(JSON -> 객체)
-역직렬화 시킬때 사용할 클래스 생성
=>예시
data class Person(
@SerializedName("person_name")
val name: String
)
(1)필드명과 JSON키 이름이 다를경우 @SerializedName으로 매핑
(2)필요없는 필드에 대해서는 클래스에 포함시키지 않는 방식으로 역직렬화에서 제외시킬 수 있다.
4)Retrofit
<1>Retrofit: REST API의 HTTP요청을 자바 인터페이스로 변환하는 것을 주 목적으로 함
<2>장점
[1]코드 간결성
[2]안정성과 확정성
-OkHttp 라이브러리로 안정적인 통신
-인터셉터로 요청, 응답 프로세스를 확장 및 수정 가능
[3]다양한 플러그인과 컨버터 지원
-다양한 데이터 형식(JSON, XML등)에 대한 데이터 변환 컨버터 제공
-Rxjava, Coroutines와 같은 비동기 프로그래밍 라이브러리와 연동 가능
<3>사용법
[1]시작
-Gradle에 Retrofit라이브러리 추가
@libs.version.toml사용법
-[verision]에 사용한 버전 변수 생성
[versions]
.
.
retrofit = "2.11.0"
okhttp3 = "4.12.0"
.
.
-[libraries]에 원래 implementation에 들어갈 부분이 "imple~:1.3.5" 꼴이면 앞을 module에 넣고 버전은 위에서 선언한 것을 가져와 뒤에다 넣으면 된다.
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp3"}
okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp3"}
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
retrofit-converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
-마지막으로 [bundles]로 묶여놓으면 된다.
[bundles]
retrofit = [
"retrofit",
"retrofit-converter-gson",
"okhttp",
"okhttp-logging"
]
-이제 bundles에 있는 retrofit을 gradle에서 implementation해주면 끝
implementation(libs.bundles.retrofit)
[2]역직렬화에 사용될 데이터클래스 정의: 이때 변수들은 nullable로 만들
data class ImageResponse(
@SerializedName("meta") val meta: MetaResponse?,
@SerializedName("documents") val documents: List<ImageDocumentResponse>?,
)
data class MetaResponse(
@SerializedName("total_count") val totalCount: Int?,
@SerializedName("pageable_count") val pageableCount: Int?,
@SerializedName("is_end") val isEnd: Boolean?,
)
data class ImageDocumentResponse(
@SerializedName("collection") val collection: String?,
@SerializedName("thumbnail_url") val thumbnailUrl: String?,
@SerializedName("image_url") val imageUrl: String?,
@SerializedName("width") val width: Int?,
@SerializedName("height") val height: Int?,
@SerializedName("display_sitename") val displaySitename: String?,
@SerializedName("doc_url") val docUrl: String?,
@SerializedName("datetime") val datetime: Date?,
val isFavorite : Boolean = false
)
[3]API 인터페이스 정의: 인터페이스로 작성된 것을 Retrofit에 전달하면 알아서 실제 함수를 구현
=>실제 네트워킹을 진행해주는 Service객체로 변
interface SearchUserImageList {
@GET("/v2/search/image")
suspend fun getSearchImage(
@Query("query") query: String,
@Query("sort") sort: String = "accuracy",
@Query("page") page: Int = 1,
@Query("size") size: Int = 10,
): ImageResponse
}
-API문서에서 base를 제외한 GET인 경우에 대한 URL을 GET에 넣어줌(다른 HTTP프로토콜 메소드도 마찬가지)
=>사용하고자 하는 메소드 위에다가
=>통신관련 메소드이므로 코루틴스코프에서 진행돼야 하니 suspend
=>@Query는 API문서에 나와있는것을 쓰면 됨
=>결과값은 역직렬화된 값으로 나와야 하므로 해당 클래스를 결과 타입으로 설정
[4]RetrofitClient 오브젝트 클래스 생성: Retrofit은 여러번 생성될 필요가 없으므로 싱글톤으로 구현
-베이스 URL 변수 선언
private const val BASE_URL = "https://dapi.kakao.com"
-네트워크 요청을 위한 httpClient구성
private val okHttpClient by lazy {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
OkHttpClient.Builder()
.addInterceptor(AuthorizationInterceptor())
.addNetworkInterceptor(interceptor)
.build()
}
-Interceptor: 앱 - OkHttp core -네트워크 구조로 존재할때 코어와 앱사이에서 요청이나 응답, 혹은 네트워크와 코어사이에서의 요청이나 응답을 가로채서 특정 작업을 하는것.
=>앱-OkHttp core 사이는 Interceptor
=>OkHttp core - 네트워크 사이는 NetworkInterceptor
@요청과 응답에 대한 interceptor는 okHttpClient가 생성될때 작성한다
-인터셉터 작성법 예시
class FixedContentInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
// 요청 가로채기
val originalRequest = chain.request()
// 요청 본문을 고정된 값으로 설정
val fixedRequestBody = RequestBody.create(MediaType.parse("text/plain"), "Fixed request body")
val modifiedRequest = originalRequest.newBuilder()
.method(originalRequest.method, fixedRequestBody)
.build()
// 수정된 요청 전송
val response = chain.proceed(modifiedRequest)
// 응답 본문을 고정된 값으로 설정
val fixedResponseBody = ResponseBody.create(response.body?.contentType(), "Fixed response body")
// 수정된 응답 반환
return response.newBuilder()
.body(fixedResponseBody)
.build()
}
}
-연결, 읽기, 쓰기에대해 timeout설정을 넣을 수 있다
return OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.addNetworkInterceptor(interceptor)
.build()
@인터셉터 사용시 유의점: 최대한 인터셉터 수를 줄이는 방식으로 만들어야 함
=>인터셉터가 늘어날수록 커넥션까지 시간이 오래걸임
@인터셉터 처리방식: 그냥 addInterceptor끼리 처리되고 addNetworkInterceptor끼리 따로 처리된다.
=>처리 순서는 요청은 추가된 순서대로, 응답은 역순으로
@참고 자료
https://bbluecoder.medium.com/a-deep-dive-into-okhttp-interceptors-8fd8d79869ec
-HttpLoggingInterceptor(): 네트워크 요청과 응답에 대한 로그 기록을 위한 interceptor
=>로그 수준 설정: interceptor.level
{1}Level.NONE: 로그를 출력하지 않음
{2}Level.BASIC: 요청의 메소드, URL, 응답 상태코드 및 콘텐츠 길이 같은 기본적인 정보만 로그로 기록
{3}Level.HEADERS: 요청 및 응답의 헤더 정보를 로그로 기록
{4}Level.BODY: 요청의 응답 본문(body)도 포함하여 로그로 기록
=>데이터가 큰 경우 성능에 영향을 미칠 수 있음
[5]API 인터페이스 생성
val searchUserImageList : SearchUserImageList by lazy {
retrofit.create(SearchUserImageList::class.java)
}
[6]응답 처리
(1)동기식: 현재 스레드에서 실행되며, 응답이 올 때 까지 다음코드 실행이 중단
val response: Response<User> = apiService.getUser(id).execute()
(2)비동기식 요청:
{1}그냥 받아오기
interface SearchUserImageList {
@GET("/v2/search/image")
suspend fun getSearchImage(
@Query("query") query: String,
@Query("sort") sort: String = "accuracy",
@Query("page") page: Int = 1,
@Query("size") size: Int = 10,
): ImageResponse
}
-API인터페이스에서 suspend로 함수 작
private fun getUserImageList(query: String) {
viewLifecycleOwner.lifecycleScope.launch {
val userList = RetrofitClient.searchUserImageList.getSearchImage(query)
searchListAdapter.submitList(userList.documents)
}
}
-코루틴을 이용해서 응답을 받아옴
{2}요청 후 콜백으로 처리:이때 Call과 Callback은 Retrofit2로 import
interface SearchUserImageList {
@GET("/v2/search/image")
fun getSearchImage(
@Query("query") query: String,
@Query("sort") sort: String = "accuracy",
@Query("page") page: Int = 1,
@Query("size") size: Int = 10,
): Call<ImageResponse>
}
-그냥 함수로 GET을 구현하고 리턴 타입은 Call<>로 감싼다.
=>이후 성공시 onResponse에서 작업, 실패시 onFilure에서 작업
RetrofitClient.searchUserImageList.getSearchImage(query).enqueue(object: Callback<ImageResponse>{
override fun onResponse(p0: Call<ImageResponse>, p1: Response<ImageResponse>) {
TODO("Not yet implemented")
}
override fun onFailure(p0: Call<ImageResponse>, p1: Throwable) {
TODO("Not yet implemented")
}
})
2. 디버깅 - 디버깅 모드 사용하기
1)브레이크 포인트 걸기: 라인 옆에 클릭해 동그라미 포인트를 만들면됨
=>함수에 걸면 느려지니 함수말고 필요하면 내부 라인에만
2)디버그툴 사용법
Resume Program: 재생모양
다음 중단점이 있을 때까지 앱을 실행함
Show Execution Point: 가로줄 세 개
클릭하면 현재 진행 중인 코드로 이동
Step Over: 회색 밑줄 위에 꺾인 화살표
break point에서 코드의 다음 줄로 이동
Step Into: 회색 밑줄 위에 아래 화살표
메서드 호출 내에서 첫 번째 줄로 이동
Step Over 와의 차이점은,
- step over는 줄단위의 코드들에 대해 디버깅
- step into는 메서드 단위로 이동하도록
보통은 메소드 안으로 들어가 step over로 디버깅하다가, 메서드를 디버깅하기 위해 step into 하면 된다.
Evaluate Expression: 계산기모양
break point로 captured 된 변수를 이용해 expression 또는 함수 등을 실행할 수 있다.
3)디버그모드로 변수값 변경: 우클릭, set Value로 값 변경 가능